diff --git a/CHANGES.md b/CHANGES.md index 3970c9b13275..5138c7d6fd2d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -4,6 +4,7 @@ Change Log ##### Additions :tada: * Added support for new `BingMapsStyle` values `ROAD_ON_DEMAND` and `AERIAL_WITH_LABELS_ON_DEMAND`. The older versions of these, `ROAD` and `AERIAL_WITH_LABELS`, have been deprecated by Bing. [#7808](https://github.com/AnalyticalGraphicsInc/cesium/pull/7808) +* Added syntax to delete data from existing properties via CZML. [#7818](https://github.com/AnalyticalGraphicsInc/cesium/pull/7818) ### 1.57 - 2019-05-01 diff --git a/Source/DataSources/CzmlDataSource.js b/Source/DataSources/CzmlDataSource.js index ddbfe03467d3..7939208fbda6 100644 --- a/Source/DataSources/CzmlDataSource.js +++ b/Source/DataSources/CzmlDataSource.js @@ -671,7 +671,6 @@ define([ } var packedLength; - var isSampled; var unwrappedInterval; var unwrappedIntervalLength; @@ -684,6 +683,19 @@ define([ var isValue = !defined(packetData.reference) && !defined(packetData.velocityReference); var hasInterval = defined(combinedInterval) && !combinedInterval.equals(Iso8601.MAXIMUM_INTERVAL); + if (packetData.delete === true) { + // If deleting this property for all time, we can simply set to undefined and return. + if (!hasInterval) { + object[propertyName] = undefined; + return; + } + + // Deleting depends on the type of property we have. + return removePropertyData(object[propertyName], combinedInterval); + } + + var isSampled = false; + if (isValue) { unwrappedInterval = unwrapInterval(type, packetData, sourceUri); packedLength = defaultValue(type.packedLength, 1); @@ -691,11 +703,11 @@ define([ isSampled = !defined(packetData.array) && (typeof unwrappedInterval !== 'string') && (unwrappedIntervalLength > packedLength) && (type !== Object); } - //Rotation is a special case because it represents a native type (Number) - //and therefore does not need to be unpacked when loaded as a constant value. + // Rotation is a special case because it represents a native type (Number) + // and therefore does not need to be unpacked when loaded as a constant value. var needsUnpacking = typeof type.unpack === 'function' && type !== Rotation; - //Any time a constant value is assigned, it completely blows away anything else. + // Any time a constant value is assigned, it completely blows away anything else. if (!isSampled && !hasInterval) { if (isValue) { object[propertyName] = new ConstantProperty(needsUnpacking ? type.unpack(unwrappedInterval, 0) : unwrappedInterval); @@ -713,8 +725,8 @@ define([ epoch = JulianDate.fromIso8601(packetEpoch); } - //Without an interval, any sampled value is infinite, meaning it completely - //replaces any non-sampled property that may exist. + // Without an interval, any sampled value is infinite, meaning it completely + // replaces any non-sampled property that may exist. if (isSampled && !hasInterval) { if (!(property instanceof SampledProperty)) { property = new SampledProperty(type); @@ -727,11 +739,11 @@ define([ var interval; - //A constant value with an interval is normally part of a TimeIntervalCollection, - //However, if the current property is not a time-interval collection, we need - //to turn it into a Composite, preserving the old data with the new interval. + // A constant value with an interval is normally part of a TimeIntervalCollection, + // However, if the current property is not a time-interval collection, we need + // to turn it into a Composite, preserving the old data with the new interval. if (!isSampled && hasInterval) { - //Create a new interval for the constant value. + // Create a new interval for the constant value. combinedInterval = combinedInterval.clone(); if (isValue) { combinedInterval.data = needsUnpacking ? type.unpack(unwrappedInterval, 0) : unwrappedInterval; @@ -739,7 +751,7 @@ define([ combinedInterval.data = createSpecializedProperty(type, entityCollection, packetData); } - //If no property exists, simply use a new interval collection + // If no property exists, simply use a new interval collection if (!defined(property)) { if (isValue) { property = new TimeIntervalCollectionProperty(); @@ -750,29 +762,28 @@ define([ } if (isValue && property instanceof TimeIntervalCollectionProperty) { - //If we create a collection, or it already existed, use it. + // If we create a collection, or it already existed, use it. property.intervals.addInterval(combinedInterval); } else if (property instanceof CompositeProperty) { - //If the collection was already a CompositeProperty, use it. + // If the collection was already a CompositeProperty, use it. if (isValue) { combinedInterval.data = new ConstantProperty(combinedInterval.data); } property.intervals.addInterval(combinedInterval); } else { - //Otherwise, create a CompositeProperty but preserve the existing data. - - //Put the old property in an infinite interval. + // Otherwise, create a CompositeProperty but preserve the existing data. + // Put the old property in an infinite interval. interval = Iso8601.MAXIMUM_INTERVAL.clone(); interval.data = property; - //Create the composite. + // Create the composite. property = new CompositeProperty(); object[propertyName] = property; - //add the old property interval + // Add the old property interval. property.intervals.addInterval(interval); - //Change the new data to a ConstantProperty and add it. + // Change the new data to a ConstantProperty and add it. if (isValue) { combinedInterval.data = new ConstantProperty(combinedInterval.data); } @@ -788,25 +799,25 @@ define([ object[propertyName] = property; } - //create a CompositeProperty but preserve the existing data. + // Create a CompositeProperty but preserve the existing data. if (!(property instanceof CompositeProperty)) { - //Put the old property in an infinite interval. + // Put the old property in an infinite interval. interval = Iso8601.MAXIMUM_INTERVAL.clone(); interval.data = property; - //Create the composite. + // Create the composite. property = new CompositeProperty(); object[propertyName] = property; - //add the old property interval + // Add the old property interval. property.intervals.addInterval(interval); } - //Check if the interval already exists in the composite + // Check if the interval already exists in the composite. var intervals = property.intervals; interval = intervals.findInterval(combinedInterval); if (!defined(interval) || !(interval.data instanceof SampledProperty)) { - //If not, create a SampledProperty for it. + // If not, create a SampledProperty for it. interval = combinedInterval.clone(); interval.data = new SampledProperty(type); intervals.addInterval(interval); @@ -815,6 +826,28 @@ define([ updateInterpolationSettings(packetData, interval.data); } + function removePropertyData(property, interval) { + if (property instanceof SampledProperty) { + property.removeSamples(interval); + return; + } else if (property instanceof TimeIntervalCollectionProperty) { + property.intervals.removeInterval(interval); + return; + } else if (property instanceof CompositeProperty) { + var intervals = property.intervals; + for (var i = 0; i < intervals.length; ++i) { + var intersection = TimeInterval.intersect(intervals.get(i), interval, scratchTimeInterval); + if (!intersection.isEmpty) { + // remove data from the contained properties + removePropertyData(intersection.data, interval); + } + } + // remove the intervals from the composite + intervals.removeInterval(interval); + return; + } + } + function processPacketData(type, object, propertyName, packetData, interval, sourceUri, entityCollection) { if (!defined(packetData)) { return; @@ -842,15 +875,27 @@ define([ combinedInterval = constrainedInterval; } - var referenceFrame; - var unwrappedInterval; - var isSampled = false; - var unwrappedIntervalLength; var numberOfDerivatives = defined(packetData.cartesianVelocity) ? 1 : 0; var packedLength = Cartesian3.packedLength * (numberOfDerivatives + 1); + var unwrappedInterval; + var unwrappedIntervalLength; var isValue = !defined(packetData.reference); var hasInterval = defined(combinedInterval) && !combinedInterval.equals(Iso8601.MAXIMUM_INTERVAL); + if (packetData.delete === true) { + // If deleting this property for all time, we can simply set to undefined and return. + if (!hasInterval) { + object[propertyName] = undefined; + return; + } + + // Deleting depends on the type of property we have. + return removePositionPropertyData(object[propertyName], combinedInterval); + } + + var referenceFrame; + var isSampled = false; + if (isValue) { if (defined(packetData.referenceFrame)) { referenceFrame = ReferenceFrame[packetData.referenceFrame]; @@ -861,7 +906,7 @@ define([ isSampled = unwrappedIntervalLength > packedLength; } - //Any time a constant value is assigned, it completely blows away anything else. + // Any time a constant value is assigned, it completely blows away anything else. if (!isSampled && !hasInterval) { if (isValue) { object[propertyName] = new ConstantPositionProperty(Cartesian3.unpack(unwrappedInterval), referenceFrame); @@ -879,8 +924,8 @@ define([ epoch = JulianDate.fromIso8601(packetEpoch); } - //Without an interval, any sampled value is infinite, meaning it completely - //replaces any non-sampled property that may exist. + // Without an interval, any sampled value is infinite, meaning it completely + // replaces any non-sampled property that may exist. if (isSampled && !hasInterval) { if (!(property instanceof SampledPositionProperty) || (defined(referenceFrame) && property.referenceFrame !== referenceFrame)) { property = new SampledPositionProperty(referenceFrame, numberOfDerivatives); @@ -893,11 +938,11 @@ define([ var interval; - //A constant value with an interval is normally part of a TimeIntervalCollection, - //However, if the current property is not a time-interval collection, we need - //to turn it into a Composite, preserving the old data with the new interval. + // A constant value with an interval is normally part of a TimeIntervalCollection, + // However, if the current property is not a time-interval collection, we need + // to turn it into a Composite, preserving the old data with the new interval. if (!isSampled && hasInterval) { - //Create a new interval for the constant value. + // Create a new interval for the constant value. combinedInterval = combinedInterval.clone(); if (isValue) { combinedInterval.data = Cartesian3.unpack(unwrappedInterval); @@ -905,7 +950,7 @@ define([ combinedInterval.data = createReferenceProperty(entityCollection, packetData.reference); } - //If no property exists, simply use a new interval collection + // If no property exists, simply use a new interval collection if (!defined(property)) { if (isValue) { property = new TimeIntervalCollectionPositionProperty(referenceFrame); @@ -916,29 +961,29 @@ define([ } if (isValue && property instanceof TimeIntervalCollectionPositionProperty && (defined(referenceFrame) && property.referenceFrame === referenceFrame)) { - //If we create a collection, or it already existed, use it. + // If we create a collection, or it already existed, use it. property.intervals.addInterval(combinedInterval); } else if (property instanceof CompositePositionProperty) { - //If the collection was already a CompositePositionProperty, use it. + // If the collection was already a CompositePositionProperty, use it. if (isValue) { combinedInterval.data = new ConstantPositionProperty(combinedInterval.data, referenceFrame); } property.intervals.addInterval(combinedInterval); } else { - //Otherwise, create a CompositePositionProperty but preserve the existing data. + // Otherwise, create a CompositePositionProperty but preserve the existing data. - //Put the old property in an infinite interval. + // Put the old property in an infinite interval. interval = Iso8601.MAXIMUM_INTERVAL.clone(); interval.data = property; - //Create the composite. + // Create the composite. property = new CompositePositionProperty(property.referenceFrame); object[propertyName] = property; - //add the old property interval + // Add the old property interval. property.intervals.addInterval(interval); - //Change the new data to a ConstantPositionProperty and add it. + // Change the new data to a ConstantPositionProperty and add it. if (isValue) { combinedInterval.data = new ConstantPositionProperty(combinedInterval.data, referenceFrame); } @@ -953,20 +998,20 @@ define([ property = new CompositePositionProperty(referenceFrame); object[propertyName] = property; } else if (!(property instanceof CompositePositionProperty)) { - //create a CompositeProperty but preserve the existing data. - //Put the old property in an infinite interval. + // Create a CompositeProperty but preserve the existing data. + // Put the old property in an infinite interval. interval = Iso8601.MAXIMUM_INTERVAL.clone(); interval.data = property; - //Create the composite. + // Create the composite. property = new CompositePositionProperty(property.referenceFrame); object[propertyName] = property; - //add the old property interval + // Add the old property interval. property.intervals.addInterval(interval); } - //Check if the interval already exists in the composite + //Check if the interval already exists in the composite. var intervals = property.intervals; interval = intervals.findInterval(combinedInterval); if (!defined(interval) || !(interval.data instanceof SampledPositionProperty) || (defined(referenceFrame) && interval.data.referenceFrame !== referenceFrame)) { @@ -979,6 +1024,28 @@ define([ updateInterpolationSettings(packetData, interval.data); } + function removePositionPropertyData(property, interval) { + if (property instanceof SampledPositionProperty) { + property.removeSamples(interval); + return; + } else if (property instanceof TimeIntervalCollectionPositionProperty) { + property.intervals.removeInterval(interval); + return; + } else if (property instanceof CompositePositionProperty) { + var intervals = property.intervals; + for (var i = 0; i < intervals.length; ++i) { + var intersection = TimeInterval.intersect(intervals.get(i), interval, scratchTimeInterval); + if (!intersection.isEmpty) { + // remove data from the contained properties + removePositionPropertyData(intersection.data, interval); + } + } + // remove the intervals from the composite + intervals.removeInterval(interval); + return; + } + } + function processPositionPacketData(object, propertyName, packetData, interval, sourceUri, entityCollection) { if (!defined(packetData)) { return; diff --git a/Specs/DataSources/CzmlDataSourceSpec.js b/Specs/DataSources/CzmlDataSourceSpec.js index b7c6b8d9cfe3..8eca29210e65 100644 --- a/Specs/DataSources/CzmlDataSourceSpec.js +++ b/Specs/DataSources/CzmlDataSourceSpec.js @@ -27,9 +27,17 @@ defineSuite([ 'Core/Transforms', 'Core/TranslationRotationScale', 'DataSources/CompositeEntityCollection', + 'DataSources/CompositePositionProperty', + 'DataSources/CompositeProperty', + 'DataSources/ConstantPositionProperty', + 'DataSources/ConstantProperty', 'DataSources/EntityCollection', 'DataSources/ReferenceProperty', + 'DataSources/SampledPositionProperty', + 'DataSources/SampledProperty', 'DataSources/StripeOrientation', + 'DataSources/TimeIntervalCollectionPositionProperty', + 'DataSources/TimeIntervalCollectionProperty', 'Scene/ColorBlendMode', 'Scene/HeightReference', 'Scene/HorizontalOrigin', @@ -66,9 +74,17 @@ defineSuite([ Transforms, TranslationRotationScale, CompositeEntityCollection, + CompositePositionProperty, + CompositeProperty, + ConstantPositionProperty, + ConstantProperty, EntityCollection, ReferenceProperty, + SampledPositionProperty, + SampledProperty, StripeOrientation, + TimeIntervalCollectionPositionProperty, + TimeIntervalCollectionProperty, ColorBlendMode, HeightReference, HorizontalOrigin, @@ -535,14 +551,16 @@ defineSuite([ it('can handle aligned axis expressed as a velocity reference', function() { var packet = { - 'position' : { - 'epoch' : '2016-06-17T12:00:00Z', - 'cartesian' : [0, 1, 2, 3, - 60, 61, 122, 183] + position: { + epoch: '2016-06-17T12:00:00Z', + cartesian: [ + 0, 1, 2, 3, + 60, 61, 122, 183 + ] }, - 'billboard' : { - 'alignedAxis' : { - 'velocityReference' : '#position' + billboard: { + alignedAxis: { + velocityReference: '#position' } } }; @@ -562,26 +580,21 @@ defineSuite([ it('can handle aligned axis expressed as a velocity reference within an interval', function() { var packet = { - 'position': { - 'epoch': '2016-06-17T12:00:00Z', - 'cartesian': [0, 1, 2, 3, - 60, 61, 122, 183] - }, - 'billboard': { - 'alignedAxis': [ - { - 'interval': '2016-06-17T12:00:00Z/2016-06-17T12:00:30Z', - 'unitCartesian': [ - 0, - 1, - 0 - ] - }, - { - 'interval': '2016-06-17T12:00:30Z/2016-06-17T12:01:00Z', - 'velocityReference': '#position' - } + position: { + epoch: '2016-06-17T12:00:00Z', + cartesian: [ + 0, 1, 2, 3, + 60, 61, 122, 183 ] + }, + billboard: { + alignedAxis: [{ + interval: '2016-06-17T12:00:00Z/2016-06-17T12:00:30Z', + unitCartesian: [0, 1, 0] + }, { + interval: '2016-06-17T12:00:30Z/2016-06-17T12:01:00Z', + velocityReference: '#position' + }] } }; @@ -1472,13 +1485,13 @@ defineSuite([ it('can handle orientation expressed as a velocity reference', function() { var packet = { - 'position' : { - 'epoch' : '2016-06-17T12:00:00Z', - 'cartesian' : [0, 1, 2, 3, - 60, 61, 122, 183] + position: { + epoch: '2016-06-17T12:00:00Z', + cartesian: [0, 1, 2, 3, + 60, 61, 122, 183] }, - 'orientation': { - 'velocityReference': '#position' + orientation: { + velocityReference: '#position' } }; @@ -1827,42 +1840,33 @@ defineSuite([ custom_array_constant: { array: [1, 2, 3] }, - custom_array_interval: [ - { - interval: interval1, - array: [1, 2, 3] - }, - { - interval: interval2, - array: [4, 5, 6] - } - ], + custom_array_interval: [{ + interval: interval1, + array: [1, 2, 3] + }, { + interval: interval2, + array: [4, 5, 6] + }], custom_boolean_constant: { boolean: true }, - custom_boolean_interval: [ - { - interval: interval1, - boolean: true - }, - { - interval: interval2, - boolean: false - } - ], + custom_boolean_interval: [{ + interval: interval1, + boolean: true + }, { + interval: interval2, + boolean: false + }], custom_boundingRectangle_constant: { boundingRectangle: [20, 30, 10, 11] }, - custom_boundingRectangle_interval: [ - { - interval: interval1, - boundingRectangle: [20, 30, 10, 11] - }, - { - interval: interval2, - boundingRectangle: [21, 31, 11, 12] - } - ], + custom_boundingRectangle_interval: [{ + interval: interval1, + boundingRectangle: [20, 30, 10, 11] + }, { + interval: interval2, + boundingRectangle: [21, 31, 11, 12] + }], custom_boundingRectangle_sampled: { epoch: '2012-06-01', boundingRectangle: [ @@ -1873,16 +1877,13 @@ defineSuite([ custom_cartesian2_constant: { cartesian2: [20, 30] }, - custom_cartesian2_interval: [ - { - interval: interval1, - cartesian2: [20, 30] - }, - { - interval: interval2, - cartesian2: [21, 31] - } - ], + custom_cartesian2_interval: [{ + interval: interval1, + cartesian2: [20, 30] + }, { + interval: interval2, + cartesian2: [21, 31] + }], custom_cartesian2_sampled: { epoch: '2012-06-01', cartesian2: [ @@ -1893,16 +1894,13 @@ defineSuite([ custom_cartesian_constant: { cartesian: [10, 11, 12] }, - custom_cartesian_interval: [ - { - interval: interval1, - cartesian: [10, 11, 12] - }, - { - interval: interval2, - cartesian: [13, 14, 15] - } - ], + custom_cartesian_interval: [{ + interval: interval1, + cartesian: [10, 11, 12] + }, { + interval: interval2, + cartesian: [13, 14, 15] + }], custom_cartesian_sampled: { epoch: '2012-06-01', cartesian: [ @@ -1913,16 +1911,13 @@ defineSuite([ custom_color_constant: { rgbaf: [0.1, 0.2, 0.3, 0.4] }, - custom_color_interval: [ - { - interval: interval1, - rgbaf: [0.1, 0.2, 0.3, 0.4] - }, - { - interval: interval2, - rgbaf: [0.5, 0.6, 0.7, 0.8] - } - ], + custom_color_interval: [{ + interval: interval1, + rgbaf: [0.1, 0.2, 0.3, 0.4] + }, { + interval: interval2, + rgbaf: [0.5, 0.6, 0.7, 0.8] + }], custom_color_sampled: { epoch: '2012-06-01', rgbaf: [ @@ -1933,16 +1928,13 @@ defineSuite([ custom_date_constant: { date: '2014-06-01' }, - custom_date_interval: [ - { - interval: interval1, - date: '2014-06-01' - }, - { - interval: interval2, - date: '2015-06-01' - } - ] + custom_date_interval: [{ + interval: interval1, + date: '2014-06-01' + }, { + interval: interval2, + date: '2015-06-01' + }] } }; @@ -2038,6 +2030,552 @@ defineSuite([ }); }); + it('can delete an entire property', function() { + function createDataSource() { + var packets = [{ + id: 'document', + version: '1.0' + }, { + id: 'test-constant', + billboard: { + scale: 1 + } + }, { + id: 'test-interval', + billboard: { + scale: [{ + interval: '2012-03-15T10:00:00Z/2012-03-16T10:00:00Z', + number: 1 + }] + } + }, { + id: 'test-sampled', + billboard: { + scale: { + number: ['2012-03-15T10:00:00Z', 1] + } + } + }]; + var dataSource = new CzmlDataSource(); + return dataSource.load(packets); + } + + var deletePackets = [{ + id: 'test-constant', + billboard: { + scale: { + delete: true + } + } + }, { + id: 'test-interval', + billboard: { + scale: { + delete: true + } + } + }, { + id: 'test-sampled', + billboard: { + scale: { + delete: true + } + } + }]; + + var deleteMaxIntervalPackets = [{ + id: 'test-constant', + billboard: { + scale: { + interval: TimeInterval.toIso8601(Iso8601.MAXIMUM_INTERVAL), + delete: true + } + } + }, { + id: 'test-interval', + billboard: { + scale: { + interval: TimeInterval.toIso8601(Iso8601.MAXIMUM_INTERVAL), + delete: true + } + } + }, { + id: 'test-sampled', + billboard: { + scale: { + interval: TimeInterval.toIso8601(Iso8601.MAXIMUM_INTERVAL), + delete: true + } + } + }]; + + function expectPropertiesToBeDefined(dataSource) { + var entity = dataSource.entities.getById('test-constant'); + expect(entity.billboard.scale).toBeInstanceOf(ConstantProperty); + entity = dataSource.entities.getById('test-interval'); + expect(entity.billboard.scale).toBeInstanceOf(TimeIntervalCollectionProperty); + entity = dataSource.entities.getById('test-sampled'); + expect(entity.billboard.scale).toBeInstanceOf(SampledProperty); + return dataSource; + } + + function expectPropertiesToBeUndefined(dataSource) { + var entity = dataSource.entities.getById('test-constant'); + expect(entity.billboard.scale).toBeUndefined(); + entity = dataSource.entities.getById('test-interval'); + expect(entity.billboard.scale).toBeUndefined(); + entity = dataSource.entities.getById('test-sampled'); + expect(entity.billboard.scale).toBeUndefined(); + return dataSource; + } + + return createDataSource() + .then(expectPropertiesToBeDefined) + .then(function(dataSource) { + // delete with no interval specified should delete the properties entirely + return dataSource.process(deletePackets); + }) + .then(expectPropertiesToBeUndefined) + .then(function(dataSource) { + // deleting properties that don't exist should be a no-op + return dataSource.process(deletePackets); + }) + .then(expectPropertiesToBeUndefined) + .then(createDataSource) // start over with a new data source + .then(function(dataSource) { + // delete with maximum interval specified should delete the properties entirely + return dataSource.process(deleteMaxIntervalPackets); + }) + .then(expectPropertiesToBeUndefined) + .then(function(dataSource) { + // deleting properties that don't exist should be a no-op + return dataSource.process(deleteMaxIntervalPackets); + }) + .then(expectPropertiesToBeUndefined); + }); + + it('can delete an entire position property', function() { + function createDataSource() { + var packets = [{ + id: 'document', + version: '1.0' + }, { + id: 'test-constant', + position: { + cartographicDegrees: [34, 117, 10000] + } + }, { + id: 'test-interval', + position: [{ + interval: '2012-03-15T10:00:00Z/2012-03-16T10:00:00Z', + cartographicDegrees: [34, 117, 10000] + }] + + }, { + id: 'test-sampled', + position: { + cartographicDegrees: ['2012-03-15T10:00:00Z', 34, 117, 10000] + } + }]; + var dataSource = new CzmlDataSource(); + return dataSource.load(packets); + } + + var deletePackets = [{ + id: 'test-constant', + position: { + delete: true + } + }, { + id: 'test-interval', + position: { + delete: true + } + }, { + id: 'test-sampled', + position: { + delete: true + } + }]; + + var deleteMaxIntervalPackets = [{ + id: 'test-constant', + position: { + interval: TimeInterval.toIso8601(Iso8601.MAXIMUM_INTERVAL), + delete: true + } + }, { + id: 'test-interval', + position: { + interval: TimeInterval.toIso8601(Iso8601.MAXIMUM_INTERVAL), + delete: true + } + }, { + id: 'test-sampled', + position: { + interval: TimeInterval.toIso8601(Iso8601.MAXIMUM_INTERVAL), + delete: true + } + }]; + + function expectPropertiesToBeDefined(dataSource) { + var entity = dataSource.entities.getById('test-constant'); + expect(entity.billboard.scale).toBeInstanceOf(ConstantProperty); + entity = dataSource.entities.getById('test-interval'); + expect(entity.billboard.scale).toBeInstanceOf(TimeIntervalCollectionProperty); + entity = dataSource.entities.getById('test-sampled'); + expect(entity.billboard.scale).toBeInstanceOf(SampledProperty); + return dataSource; + } + + function expectPropertiesToBeUndefined(dataSource) { + var entity = dataSource.entities.getById('test-constant'); + expect(entity.billboard.scale).toBeUndefined(); + entity = dataSource.entities.getById('test-interval'); + expect(entity.billboard.scale).toBeUndefined(); + entity = dataSource.entities.getById('test-sampled'); + expect(entity.billboard.scale).toBeUndefined(); + return dataSource; + } + + createDataSource() + .then(expectPropertiesToBeDefined) + .then(function(dataSource) { + // delete with no interval specified should delete the properties entirely + return dataSource.process(deletePackets); + }) + .then(expectPropertiesToBeUndefined) + .then(function(dataSource) { + // deleting properties that don't exist should be a no-op + return dataSource.process(deletePackets); + }) + .then(expectPropertiesToBeUndefined) + .then(createDataSource) // start over with a new data source + .then(function(dataSource) { + // delete with maximum interval specified should delete the properties entirely + return dataSource.process(deleteMaxIntervalPackets); + }) + .then(expectPropertiesToBeUndefined) + .then(function(dataSource) { + // deleting properties that don't exist should be a no-op + return dataSource.process(deleteMaxIntervalPackets); + }) + .then(expectPropertiesToBeUndefined); + }); + + it('can delete samples from a sampled property', function() { + var packet = { + id: 'id', + billboard: { + scale: { + number: [ + '2012-03-15T10:00:00Z', 1, + '2012-03-15T11:00:00Z', 5, + '2012-03-15T12:00:00Z', 3 + ] + } + } + }; + + return CzmlDataSource.load(makePacket(packet)) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.billboard.scale).toBeInstanceOf(SampledProperty); + + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2012-03-15T10:00:00Z'))).toEqual(1); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2012-03-15T11:00:00Z'))).toEqual(5); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2012-03-15T12:00:00Z'))).toEqual(3); + + return dataSource; + }) + .then(function(dataSource) { + var deletePacket = { + id: 'id', + billboard: { + scale: { + interval: '2012-03-15T11:00:00Z/2012-03-15T11:00:00Z', + delete: true + } + } + }; + return dataSource.process(deletePacket); + }) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.billboard.scale).toBeInstanceOf(SampledProperty); + + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2012-03-15T10:00:00Z'))).toEqual(1); + // deleting sample will cause the property to interpolate from remaining samples + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2012-03-15T11:00:00Z'))).toEqual(2); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2012-03-15T12:00:00Z'))).toEqual(3); + }); + }); + + it('can delete samples from a sampled position property', function() { + var packet = { + id: 'id', + position: { + epoch: '2016-06-17T12:00:00Z', + cartesian: [ + 0, 1, 2, 3, + 60, 61, 122, 183, + 120, 3, 4, 5 + ] + } + }; + + return CzmlDataSource.load(makePacket(packet)) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.position).toBeInstanceOf(SampledPositionProperty); + + expect(entity.position.getValue(JulianDate.fromIso8601('2016-06-17T12:00:00Z'))).toEqual(new Cartesian3(1, 2, 3)); + expect(entity.position.getValue(JulianDate.fromIso8601('2016-06-17T12:01:00Z'))).toEqual(new Cartesian3(61, 122, 183)); + expect(entity.position.getValue(JulianDate.fromIso8601('2016-06-17T12:02:00Z'))).toEqual(new Cartesian3(3, 4, 5)); + + return dataSource; + }) + .then(function(dataSource) { + var deletePacket = { + id: 'id', + position: { + interval: '2016-06-17T12:00:45Z/2016-06-17T12:01:10Z', + delete: true + } + }; + return dataSource.process(deletePacket); + }) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.position).toBeInstanceOf(SampledPositionProperty); + + expect(entity.position.getValue(JulianDate.fromIso8601('2016-06-17T12:00:00Z'))).toEqual(new Cartesian3(1, 2, 3)); + // deleting sample will cause the property to interpolate from remaining samples + expect(entity.position.getValue(JulianDate.fromIso8601('2016-06-17T12:01:00Z'))).toEqual(new Cartesian3(2, 3, 4)); + expect(entity.position.getValue(JulianDate.fromIso8601('2016-06-17T12:02:00Z'))).toEqual(new Cartesian3(3, 4, 5)); + }); + }); + + it('can delete interval from an interval property', function() { + var packet = { + id: 'id', + billboard: { + scale: [{ + interval: '2013-01-01T00:00:00Z/2013-01-01T01:00:00Z', + number: 2 + }, { + interval: '2013-01-01T01:00:00Z/2013-01-01T02:00:00Z', + number: 6 + }] + } + }; + + return CzmlDataSource.load(makePacket(packet)) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.billboard.scale).toBeInstanceOf(TimeIntervalCollectionProperty); + + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T00:30:00Z'))).toEqual(2); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T01:30:00Z'))).toEqual(6); + + return dataSource; + }) + .then(function(dataSource) { + var deletePacket = { + id: 'id', + billboard: { + scale: { + interval: '2013-01-01T00:30:00Z/2013-01-01T01:30:00Z', + delete: true + } + } + }; + return dataSource.process(deletePacket); + }) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.billboard.scale).toBeInstanceOf(TimeIntervalCollectionProperty); + + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T00:30:00Z'))).toBeUndefined(); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T01:30:00Z'))).toBeUndefined(); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T00:29:00Z'))).toEqual(2); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T01:31:00Z'))).toEqual(6); + }); + }); + + it('can delete interval from an interval position property', function() { + var packet = { + id: 'id', + position: [{ + interval: '2013-01-01T00:00:00Z/2013-01-01T01:00:00Z', + cartesian: [1, 2, 3] + }, { + interval: '2013-01-01T01:00:00Z/2013-01-01T02:00:00Z', + cartesian: [4, 5, 6] + }] + }; + + return CzmlDataSource.load(makePacket(packet)) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.position).toBeInstanceOf(TimeIntervalCollectionPositionProperty); + + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T00:30:00Z'))).toEqual(new Cartesian3(1, 2, 3)); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T01:30:00Z'))).toEqual(new Cartesian3(4, 5, 6)); + + return dataSource; + }) + .then(function(dataSource) { + var deletePacket = { + id: 'id', + position: { + interval: '2013-01-01T00:30:00Z/2013-01-01T01:30:00Z', + delete: true + } + }; + return dataSource.process(deletePacket); + }) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.position).toBeInstanceOf(TimeIntervalCollectionPositionProperty); + + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T00:30:00Z'))).toBeUndefined(); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T01:30:00Z'))).toBeUndefined(); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T00:29:00Z'))).toEqual(new Cartesian3(1, 2, 3)); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T01:31:00Z'))).toEqual(new Cartesian3(4, 5, 6)); + }); + }); + + it('can delete samples from a composite property', function() { + var packet = { + id: 'id', + billboard: { + scale: [{ + interval: '2013-01-01T00:00:00Z/2013-01-01T01:00:00Z', + epoch: '2013-01-01T00:00:00Z', + number: [ + 0, 1, + 30, 6, + 60, 3 + ] + }, { + interval: '2013-01-01T00:02:00Z/2013-01-01T01:00:00Z', + number: 33 + }, { + interval: '2013-01-01T01:00:00Z/2013-01-01T02:00:00Z', + number: [ + '2013-01-01T01:00:00Z', 9, + '2013-01-01T01:00:30Z', 19, + '2013-01-01T01:01:00Z', 11 + ] + }] + } + }; + + return CzmlDataSource.load(makePacket(packet)) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.billboard.scale).toBeInstanceOf(CompositeProperty); + + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T00:00:00Z'))).toEqual(1); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T00:00:30Z'))).toEqual(6); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T00:01:00Z'))).toEqual(3); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T00:02:00Z'))).toEqual(33); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T01:00:00Z'))).toEqual(9); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T01:00:30Z'))).toEqual(19); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T01:01:00Z'))).toEqual(11); + + return dataSource; + }) + .then(function(dataSource) { + var deletePacket = { + id: 'id', + billboard: { + scale: { + interval: '2013-01-01T00:01:00Z/2013-01-01T01:00:00Z', + delete: true + } + } + }; + return dataSource.process(deletePacket); + }) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.billboard.scale).toBeInstanceOf(CompositeProperty); + + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T00:00:00Z'))).toEqual(1); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T00:00:30Z'))).toEqual(6); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T00:01:00Z'))).toBeUndefined(); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T00:02:00Z'))).toBeUndefined(); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T01:00:00Z'))).toBeUndefined(); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T01:00:30Z'))).toEqual(19); + expect(entity.billboard.scale.getValue(JulianDate.fromIso8601('2013-01-01T01:01:00Z'))).toEqual(11); + }); + }); + + it('can delete samples from a composite position property', function() { + var packet = { + id: 'id', + position: [{ + interval: '2013-01-01T00:00:00Z/2013-01-01T01:00:00Z', + epoch: '2013-01-01T00:00:00Z', + cartesian: [ + 0, 1, 2, 3, + 30, 6, 7, 8, + 60, 3, 4, 5 + ] + }, { + interval: '2013-01-01T00:02:00Z/2013-01-01T01:00:00Z', + cartesian: [15, 16, 17] + }, { + interval: '2013-01-01T01:00:00Z/2013-01-01T02:00:00Z', + cartesian: [ + '2013-01-01T01:00:00Z', 9, 15, 10, + '2013-01-01T01:00:30Z', 19, 16, 11, + '2013-01-01T01:01:00Z', 11, 17, 12 + ] + }] + }; + + return CzmlDataSource.load(makePacket(packet)) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.position).toBeInstanceOf(CompositePositionProperty); + + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T00:00:00Z'))).toEqual(new Cartesian3(1, 2, 3)); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T00:00:30Z'))).toEqual(new Cartesian3(6, 7, 8)); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T00:01:00Z'))).toEqual(new Cartesian3(3, 4, 5)); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T00:02:00Z'))).toEqual(new Cartesian3(15, 16, 17)); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T01:00:00Z'))).toEqual(new Cartesian3(9, 15, 10)); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T01:00:30Z'))).toEqual(new Cartesian3(19, 16, 11)); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T01:01:00Z'))).toEqual(new Cartesian3(11, 17, 12)); + + return dataSource; + }) + .then(function(dataSource) { + var deletePacket = { + id: 'id', + position: { + interval: '2013-01-01T00:01:00Z/2013-01-01T01:00:00Z', + delete: true + } + }; + return dataSource.process(deletePacket); + }) + .then(function(dataSource) { + var entity = dataSource.entities.getById('id'); + expect(entity.position).toBeInstanceOf(CompositePositionProperty); + + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T00:00:00Z'))).toEqual(new Cartesian3(1, 2, 3)); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T00:00:30Z'))).toEqual(new Cartesian3(6, 7, 8)); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T00:01:00Z'))).toBeUndefined(); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T00:02:00Z'))).toBeUndefined(); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T01:00:00Z'))).toBeUndefined(); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T01:00:30Z'))).toEqual(new Cartesian3(19, 16, 11)); + expect(entity.position.getValue(JulianDate.fromIso8601('2013-01-01T01:01:00Z'))).toEqual(new Cartesian3(11, 17, 12)); + }); + }); + it('handles properties in a way that allows CompositeEntityCollection to work', function() { var testObject1 = { foo: 4,