UNPKG

@cesium/engine

Version:

CesiumJS is a JavaScript library for creating 3D globes and 2D maps in a web browser without a plugin.

1,644 lines (1,521 loc) 124 kB
import ArcType from "../Core/ArcType.js"; import BoundingRectangle from "../Core/BoundingRectangle.js"; import Cartesian2 from "../Core/Cartesian2.js"; import Cartesian3 from "../Core/Cartesian3.js"; import Cartographic from "../Core/Cartographic.js"; import ClockRange from "../Core/ClockRange.js"; import ClockStep from "../Core/ClockStep.js"; import Color from "../Core/Color.js"; import CornerType from "../Core/CornerType.js"; import Credit from "../Core/Credit.js"; import createGuid from "../Core/createGuid.js"; import Frozen from "../Core/Frozen.js"; import defined from "../Core/defined.js"; import DeveloperError from "../Core/DeveloperError.js"; import DistanceDisplayCondition from "../Core/DistanceDisplayCondition.js"; import Ellipsoid from "../Core/Ellipsoid.js"; import Event from "../Core/Event.js"; import ExtrapolationType from "../Core/ExtrapolationType.js"; import getFilenameFromUri from "../Core/getFilenameFromUri.js"; import HermitePolynomialApproximation from "../Core/HermitePolynomialApproximation.js"; import Iso8601 from "../Core/Iso8601.js"; import JulianDate from "../Core/JulianDate.js"; import LagrangePolynomialApproximation from "../Core/LagrangePolynomialApproximation.js"; import LinearApproximation from "../Core/LinearApproximation.js"; import CesiumMath from "../Core/Math.js"; import NearFarScalar from "../Core/NearFarScalar.js"; import PolygonHierarchy from "../Core/PolygonHierarchy.js"; import Quaternion from "../Core/Quaternion.js"; import Rectangle from "../Core/Rectangle.js"; import ReferenceFrame from "../Core/ReferenceFrame.js"; import Resource from "../Core/Resource.js"; import RuntimeError from "../Core/RuntimeError.js"; import Spherical from "../Core/Spherical.js"; import TimeInterval from "../Core/TimeInterval.js"; import TimeIntervalCollection from "../Core/TimeIntervalCollection.js"; import ClassificationType from "../Scene/ClassificationType.js"; import ColorBlendMode from "../Scene/ColorBlendMode.js"; import HeightReference from "../Scene/HeightReference.js"; import HorizontalOrigin from "../Scene/HorizontalOrigin.js"; import LabelStyle from "../Scene/LabelStyle.js"; import ShadowMode from "../Scene/ShadowMode.js"; import VerticalOrigin from "../Scene/VerticalOrigin.js"; import Uri from "urijs"; import BillboardGraphics from "./BillboardGraphics.js"; import BoxGraphics from "./BoxGraphics.js"; import CallbackProperty from "./CallbackProperty.js"; import CheckerboardMaterialProperty from "./CheckerboardMaterialProperty.js"; import ColorMaterialProperty from "./ColorMaterialProperty.js"; import CompositeMaterialProperty from "./CompositeMaterialProperty.js"; import CompositePositionProperty from "./CompositePositionProperty.js"; import CompositeProperty from "./CompositeProperty.js"; import ConstantPositionProperty from "./ConstantPositionProperty.js"; import ConstantProperty from "./ConstantProperty.js"; import CorridorGraphics from "./CorridorGraphics.js"; import CylinderGraphics from "./CylinderGraphics.js"; import DataSource from "./DataSource.js"; import DataSourceClock from "./DataSourceClock.js"; import EllipseGraphics from "./EllipseGraphics.js"; import EllipsoidGraphics from "./EllipsoidGraphics.js"; import EntityCluster from "./EntityCluster.js"; import EntityCollection from "./EntityCollection.js"; import GridMaterialProperty from "./GridMaterialProperty.js"; import ImageMaterialProperty from "./ImageMaterialProperty.js"; import LabelGraphics from "./LabelGraphics.js"; import ModelGraphics from "./ModelGraphics.js"; import NodeTransformationProperty from "./NodeTransformationProperty.js"; import PathGraphics from "./PathGraphics.js"; import PointGraphics from "./PointGraphics.js"; import PolygonGraphics from "./PolygonGraphics.js"; import PolylineArrowMaterialProperty from "./PolylineArrowMaterialProperty.js"; import PolylineDashMaterialProperty from "./PolylineDashMaterialProperty.js"; import PolylineGlowMaterialProperty from "./PolylineGlowMaterialProperty.js"; import PolylineGraphics from "./PolylineGraphics.js"; import PolylineOutlineMaterialProperty from "./PolylineOutlineMaterialProperty.js"; import PolylineVolumeGraphics from "./PolylineVolumeGraphics.js"; import PositionPropertyArray from "./PositionPropertyArray.js"; import Property from "./Property.js"; import PropertyArray from "./PropertyArray.js"; import PropertyBag from "./PropertyBag.js"; import RectangleGraphics from "./RectangleGraphics.js"; import ReferenceProperty from "./ReferenceProperty.js"; import Rotation from "./Rotation.js"; import SampledPositionProperty from "./SampledPositionProperty.js"; import SampledProperty from "./SampledProperty.js"; import StripeMaterialProperty from "./StripeMaterialProperty.js"; import StripeOrientation from "./StripeOrientation.js"; import TimeIntervalCollectionPositionProperty from "./TimeIntervalCollectionPositionProperty.js"; import TimeIntervalCollectionProperty from "./TimeIntervalCollectionProperty.js"; import VelocityOrientationProperty from "./VelocityOrientationProperty.js"; import VelocityVectorProperty from "./VelocityVectorProperty.js"; import WallGraphics from "./WallGraphics.js"; import Cesium3DTilesetGraphics from "./Cesium3DTilesetGraphics.js"; import SensorVolumePortionToDisplay from "../Scene/SensorVolumePortionToDisplay.js"; // A marker type to distinguish CZML properties where we need to end up with a unit vector. // The data is still loaded into Cartesian3 objects but they are normalized. function UnitCartesian3() {} UnitCartesian3.packedLength = Cartesian3.packedLength; UnitCartesian3.unpack = Cartesian3.unpack; UnitCartesian3.pack = Cartesian3.pack; // As a side note, for the purposes of CZML, Quaternion always indicates a unit quaternion. let currentId; function createReferenceProperty(entityCollection, referenceString) { if (referenceString[0] === "#") { referenceString = currentId + referenceString; } return ReferenceProperty.fromString(entityCollection, referenceString); } function createSpecializedProperty(type, entityCollection, packetData) { if (defined(packetData.reference)) { return createReferenceProperty(entityCollection, packetData.reference); } if (defined(packetData.velocityReference)) { const referenceProperty = createReferenceProperty( entityCollection, packetData.velocityReference, ); switch (type) { case Cartesian3: case UnitCartesian3: return new VelocityVectorProperty( referenceProperty, type === UnitCartesian3, ); case Quaternion: return new VelocityOrientationProperty(referenceProperty); } } throw new RuntimeError(`${JSON.stringify(packetData)} is not valid CZML.`); } function createAdapterProperty(property, adapterFunction) { return new CallbackProperty(function (time, result) { return adapterFunction(property.getValue(time, result)); }, property.isConstant); } const scratchCartesian = new Cartesian3(); const scratchSpherical = new Spherical(); const scratchCartographic = new Cartographic(); const scratchTimeInterval = new TimeInterval(); const scratchQuaternion = new Quaternion(); function unwrapColorInterval(czmlInterval) { let rgbaf = czmlInterval.rgbaf; if (defined(rgbaf)) { return rgbaf; } const rgba = czmlInterval.rgba; if (!defined(rgba)) { return undefined; } const length = rgba.length; if (length === Color.packedLength) { return [ Color.byteToFloat(rgba[0]), Color.byteToFloat(rgba[1]), Color.byteToFloat(rgba[2]), Color.byteToFloat(rgba[3]), ]; } rgbaf = new Array(length); for (let i = 0; i < length; i += 5) { rgbaf[i] = rgba[i]; rgbaf[i + 1] = Color.byteToFloat(rgba[i + 1]); rgbaf[i + 2] = Color.byteToFloat(rgba[i + 2]); rgbaf[i + 3] = Color.byteToFloat(rgba[i + 3]); rgbaf[i + 4] = Color.byteToFloat(rgba[i + 4]); } return rgbaf; } function unwrapUriInterval(czmlInterval, sourceUri) { const uri = czmlInterval.uri ?? czmlInterval; if (defined(sourceUri)) { return sourceUri.getDerivedResource({ url: uri, }); } return Resource.createIfNeeded(uri); } function unwrapRectangleInterval(czmlInterval) { let wsen = czmlInterval.wsen; if (defined(wsen)) { return wsen; } const wsenDegrees = czmlInterval.wsenDegrees; if (!defined(wsenDegrees)) { return undefined; } const length = wsenDegrees.length; if (length === Rectangle.packedLength) { return [ CesiumMath.toRadians(wsenDegrees[0]), CesiumMath.toRadians(wsenDegrees[1]), CesiumMath.toRadians(wsenDegrees[2]), CesiumMath.toRadians(wsenDegrees[3]), ]; } wsen = new Array(length); for (let i = 0; i < length; i += 5) { wsen[i] = wsenDegrees[i]; wsen[i + 1] = CesiumMath.toRadians(wsenDegrees[i + 1]); wsen[i + 2] = CesiumMath.toRadians(wsenDegrees[i + 2]); wsen[i + 3] = CesiumMath.toRadians(wsenDegrees[i + 3]); wsen[i + 4] = CesiumMath.toRadians(wsenDegrees[i + 4]); } return wsen; } function convertUnitSphericalToCartesian(unitSpherical) { const length = unitSpherical.length; scratchSpherical.magnitude = 1.0; if (length === 2) { scratchSpherical.clock = unitSpherical[0]; scratchSpherical.cone = unitSpherical[1]; Cartesian3.fromSpherical(scratchSpherical, scratchCartesian); return [scratchCartesian.x, scratchCartesian.y, scratchCartesian.z]; } const result = new Array((length / 3) * 4); for (let i = 0, j = 0; i < length; i += 3, j += 4) { result[j] = unitSpherical[i]; scratchSpherical.clock = unitSpherical[i + 1]; scratchSpherical.cone = unitSpherical[i + 2]; Cartesian3.fromSpherical(scratchSpherical, scratchCartesian); result[j + 1] = scratchCartesian.x; result[j + 2] = scratchCartesian.y; result[j + 3] = scratchCartesian.z; } return result; } function convertSphericalToCartesian(spherical) { const length = spherical.length; if (length === 3) { scratchSpherical.clock = spherical[0]; scratchSpherical.cone = spherical[1]; scratchSpherical.magnitude = spherical[2]; Cartesian3.fromSpherical(scratchSpherical, scratchCartesian); return [scratchCartesian.x, scratchCartesian.y, scratchCartesian.z]; } const result = new Array(length); for (let i = 0; i < length; i += 4) { result[i] = spherical[i]; scratchSpherical.clock = spherical[i + 1]; scratchSpherical.cone = spherical[i + 2]; scratchSpherical.magnitude = spherical[i + 3]; Cartesian3.fromSpherical(scratchSpherical, scratchCartesian); result[i + 1] = scratchCartesian.x; result[i + 2] = scratchCartesian.y; result[i + 3] = scratchCartesian.z; } return result; } function convertCartographicRadiansToCartesian(cartographicRadians) { const length = cartographicRadians.length; if (length === 3) { scratchCartographic.longitude = cartographicRadians[0]; scratchCartographic.latitude = cartographicRadians[1]; scratchCartographic.height = cartographicRadians[2]; Ellipsoid.default.cartographicToCartesian( scratchCartographic, scratchCartesian, ); return [scratchCartesian.x, scratchCartesian.y, scratchCartesian.z]; } const result = new Array(length); for (let i = 0; i < length; i += 4) { result[i] = cartographicRadians[i]; scratchCartographic.longitude = cartographicRadians[i + 1]; scratchCartographic.latitude = cartographicRadians[i + 2]; scratchCartographic.height = cartographicRadians[i + 3]; Ellipsoid.default.cartographicToCartesian( scratchCartographic, scratchCartesian, ); result[i + 1] = scratchCartesian.x; result[i + 2] = scratchCartesian.y; result[i + 3] = scratchCartesian.z; } return result; } function convertCartographicDegreesToCartesian(cartographicDegrees) { const length = cartographicDegrees.length; if (length === 3) { scratchCartographic.longitude = CesiumMath.toRadians( cartographicDegrees[0], ); scratchCartographic.latitude = CesiumMath.toRadians(cartographicDegrees[1]); scratchCartographic.height = cartographicDegrees[2]; Ellipsoid.default.cartographicToCartesian( scratchCartographic, scratchCartesian, ); return [scratchCartesian.x, scratchCartesian.y, scratchCartesian.z]; } const result = new Array(length); for (let i = 0; i < length; i += 4) { result[i] = cartographicDegrees[i]; scratchCartographic.longitude = CesiumMath.toRadians( cartographicDegrees[i + 1], ); scratchCartographic.latitude = CesiumMath.toRadians( cartographicDegrees[i + 2], ); scratchCartographic.height = cartographicDegrees[i + 3]; Ellipsoid.default.cartographicToCartesian( scratchCartographic, scratchCartesian, ); result[i + 1] = scratchCartesian.x; result[i + 2] = scratchCartesian.y; result[i + 3] = scratchCartesian.z; } return result; } function unwrapCartesianInterval(czmlInterval) { const cartesian = czmlInterval.cartesian; if (defined(cartesian)) { return cartesian; } const cartesianVelocity = czmlInterval.cartesianVelocity; if (defined(cartesianVelocity)) { return cartesianVelocity; } const unitCartesian = czmlInterval.unitCartesian; if (defined(unitCartesian)) { return unitCartesian; } const unitSpherical = czmlInterval.unitSpherical; if (defined(unitSpherical)) { return convertUnitSphericalToCartesian(unitSpherical); } const spherical = czmlInterval.spherical; if (defined(spherical)) { return convertSphericalToCartesian(spherical); } const cartographicRadians = czmlInterval.cartographicRadians; if (defined(cartographicRadians)) { return convertCartographicRadiansToCartesian(cartographicRadians); } const cartographicDegrees = czmlInterval.cartographicDegrees; if (defined(cartographicDegrees)) { return convertCartographicDegreesToCartesian(cartographicDegrees); } throw new RuntimeError( `${JSON.stringify(czmlInterval)} is not a valid CZML interval.`, ); } function normalizePackedCartesianArray(array, startingIndex) { Cartesian3.unpack(array, startingIndex, scratchCartesian); Cartesian3.normalize(scratchCartesian, scratchCartesian); Cartesian3.pack(scratchCartesian, array, startingIndex); } function unwrapUnitCartesianInterval(czmlInterval) { const cartesian = unwrapCartesianInterval(czmlInterval); if (cartesian.length === 3) { normalizePackedCartesianArray(cartesian, 0); return cartesian; } for (let i = 1; i < cartesian.length; i += 4) { normalizePackedCartesianArray(cartesian, i); } return cartesian; } function normalizePackedQuaternionArray(array, startingIndex) { Quaternion.unpack(array, startingIndex, scratchQuaternion); Quaternion.normalize(scratchQuaternion, scratchQuaternion); Quaternion.pack(scratchQuaternion, array, startingIndex); } function unwrapQuaternionInterval(czmlInterval) { const unitQuaternion = czmlInterval.unitQuaternion; if (defined(unitQuaternion)) { if (unitQuaternion.length === 4) { normalizePackedQuaternionArray(unitQuaternion, 0); return unitQuaternion; } for (let i = 1; i < unitQuaternion.length; i += 5) { normalizePackedQuaternionArray(unitQuaternion, i); } } return unitQuaternion; } function getPropertyType(czmlInterval) { // The associations in this function need to be kept in sync with the // associations in unwrapInterval. // Intentionally omitted due to conficts in CZML property names: // * Image (conflicts with Uri) // * Rotation (conflicts with Number) // // cartesianVelocity is also omitted due to incomplete support for // derivative information in CZML properties. // (Currently cartesianVelocity is hacked directly into the position processing code) if (typeof czmlInterval === "boolean") { return Boolean; } else if (typeof czmlInterval === "number") { return Number; } else if (typeof czmlInterval === "string") { return String; } else if (czmlInterval.hasOwnProperty("array")) { return Array; } else if (czmlInterval.hasOwnProperty("boolean")) { return Boolean; } else if (czmlInterval.hasOwnProperty("boundingRectangle")) { return BoundingRectangle; } else if (czmlInterval.hasOwnProperty("cartesian2")) { return Cartesian2; } else if ( czmlInterval.hasOwnProperty("cartesian") || czmlInterval.hasOwnProperty("spherical") || czmlInterval.hasOwnProperty("cartographicRadians") || czmlInterval.hasOwnProperty("cartographicDegrees") ) { return Cartesian3; } else if ( czmlInterval.hasOwnProperty("unitCartesian") || czmlInterval.hasOwnProperty("unitSpherical") ) { return UnitCartesian3; } else if ( czmlInterval.hasOwnProperty("rgba") || czmlInterval.hasOwnProperty("rgbaf") ) { return Color; } else if (czmlInterval.hasOwnProperty("arcType")) { return ArcType; } else if (czmlInterval.hasOwnProperty("classificationType")) { return ClassificationType; } else if (czmlInterval.hasOwnProperty("colorBlendMode")) { return ColorBlendMode; } else if (czmlInterval.hasOwnProperty("cornerType")) { return CornerType; } else if (czmlInterval.hasOwnProperty("heightReference")) { return HeightReference; } else if (czmlInterval.hasOwnProperty("horizontalOrigin")) { return HorizontalOrigin; } else if (czmlInterval.hasOwnProperty("date")) { return JulianDate; } else if (czmlInterval.hasOwnProperty("labelStyle")) { return LabelStyle; } else if (czmlInterval.hasOwnProperty("number")) { return Number; } else if (czmlInterval.hasOwnProperty("nearFarScalar")) { return NearFarScalar; } else if (czmlInterval.hasOwnProperty("distanceDisplayCondition")) { return DistanceDisplayCondition; } else if ( czmlInterval.hasOwnProperty("object") || czmlInterval.hasOwnProperty("value") ) { return Object; } else if (czmlInterval.hasOwnProperty("unitQuaternion")) { return Quaternion; } else if (czmlInterval.hasOwnProperty("shadowMode")) { return ShadowMode; } else if (czmlInterval.hasOwnProperty("string")) { return String; } else if (czmlInterval.hasOwnProperty("stripeOrientation")) { return StripeOrientation; } else if ( czmlInterval.hasOwnProperty("wsen") || czmlInterval.hasOwnProperty("wsenDegrees") ) { return Rectangle; } else if (czmlInterval.hasOwnProperty("uri")) { return Uri; } else if (czmlInterval.hasOwnProperty("verticalOrigin")) { return VerticalOrigin; } // fallback case return Object; } function unwrapInterval(type, czmlInterval, sourceUri) { // The associations in this function need to be kept in sync with the // associations in getPropertyType switch (type) { case ArcType: return ArcType[czmlInterval.arcType ?? czmlInterval]; case Array: return czmlInterval.array; case Boolean: return czmlInterval["boolean"] ?? czmlInterval; case BoundingRectangle: return czmlInterval.boundingRectangle; case Cartesian2: return czmlInterval.cartesian2; case Cartesian3: return unwrapCartesianInterval(czmlInterval); case UnitCartesian3: return unwrapUnitCartesianInterval(czmlInterval); case Color: return unwrapColorInterval(czmlInterval); case ClassificationType: return ClassificationType[ czmlInterval.classificationType ?? czmlInterval ]; case ColorBlendMode: return ColorBlendMode[czmlInterval.colorBlendMode ?? czmlInterval]; case CornerType: return CornerType[czmlInterval.cornerType ?? czmlInterval]; case HeightReference: return HeightReference[czmlInterval.heightReference ?? czmlInterval]; case HorizontalOrigin: return HorizontalOrigin[czmlInterval.horizontalOrigin ?? czmlInterval]; case Image: return unwrapUriInterval(czmlInterval, sourceUri); case JulianDate: return JulianDate.fromIso8601(czmlInterval.date ?? czmlInterval); case LabelStyle: return LabelStyle[czmlInterval.labelStyle ?? czmlInterval]; case Number: return czmlInterval.number ?? czmlInterval; case NearFarScalar: return czmlInterval.nearFarScalar; case DistanceDisplayCondition: return czmlInterval.distanceDisplayCondition; case Object: return czmlInterval.object ?? czmlInterval.value ?? czmlInterval; case Quaternion: return unwrapQuaternionInterval(czmlInterval); case Rotation: return czmlInterval.number ?? czmlInterval; case SensorVolumePortionToDisplay: return SensorVolumePortionToDisplay[ czmlInterval.portionToDisplay ?? czmlInterval ]; case ShadowMode: return ShadowMode[ czmlInterval.shadowMode ?? czmlInterval.shadows ?? czmlInterval ]; case String: return czmlInterval.string ?? czmlInterval; case StripeOrientation: return StripeOrientation[czmlInterval.stripeOrientation ?? czmlInterval]; case Rectangle: return unwrapRectangleInterval(czmlInterval); case Uri: return unwrapUriInterval(czmlInterval, sourceUri); case VerticalOrigin: return VerticalOrigin[czmlInterval.verticalOrigin ?? czmlInterval]; default: throw new RuntimeError(`Unknown CzmlDataSource interval type: ${type}`); } } const interpolators = { HERMITE: HermitePolynomialApproximation, LAGRANGE: LagrangePolynomialApproximation, LINEAR: LinearApproximation, }; function updateInterpolationSettings(packetData, property) { const interpolationAlgorithm = packetData.interpolationAlgorithm; const interpolationDegree = packetData.interpolationDegree; if (defined(interpolationAlgorithm) || defined(interpolationDegree)) { property.setInterpolationOptions({ interpolationAlgorithm: interpolators[interpolationAlgorithm], interpolationDegree: interpolationDegree, }); } const forwardExtrapolationType = packetData.forwardExtrapolationType; if (defined(forwardExtrapolationType)) { property.forwardExtrapolationType = ExtrapolationType[forwardExtrapolationType]; } const forwardExtrapolationDuration = packetData.forwardExtrapolationDuration; if (defined(forwardExtrapolationDuration)) { property.forwardExtrapolationDuration = forwardExtrapolationDuration; } const backwardExtrapolationType = packetData.backwardExtrapolationType; if (defined(backwardExtrapolationType)) { property.backwardExtrapolationType = ExtrapolationType[backwardExtrapolationType]; } const backwardExtrapolationDuration = packetData.backwardExtrapolationDuration; if (defined(backwardExtrapolationDuration)) { property.backwardExtrapolationDuration = backwardExtrapolationDuration; } } const iso8601Scratch = { iso8601: undefined, }; function intervalFromString(intervalString) { if (!defined(intervalString)) { return undefined; } iso8601Scratch.iso8601 = intervalString; return TimeInterval.fromIso8601(iso8601Scratch); } function wrapPropertyInInfiniteInterval(property) { const interval = Iso8601.MAXIMUM_INTERVAL.clone(); interval.data = property; return interval; } function convertPropertyToComposite(property) { // Create the composite and add the old property, wrapped in an infinite interval. const composite = new CompositeProperty(); composite.intervals.addInterval(wrapPropertyInInfiniteInterval(property)); return composite; } function convertPositionPropertyToComposite(property) { // Create the composite and add the old property, wrapped in an infinite interval. const composite = new CompositePositionProperty(property.referenceFrame); composite.intervals.addInterval(wrapPropertyInInfiniteInterval(property)); return composite; } function processProperty( type, object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection, ) { let combinedInterval = intervalFromString(packetData.interval); if (defined(constrainedInterval)) { if (defined(combinedInterval)) { combinedInterval = TimeInterval.intersect( combinedInterval, constrainedInterval, scratchTimeInterval, ); } else { combinedInterval = constrainedInterval; } } let packedLength; let unwrappedInterval; let unwrappedIntervalLength; // CZML properties can be defined in many ways. Most ways represent a structure for // encoding a single value (number, string, cartesian, etc.) Regardless of the value type, // if it encodes a single value it will get loaded into a ConstantProperty eventually. // Alternatively, there are ways of defining a property that require specialized // client-side representation. Currently, these are ReferenceProperty, // and client-side velocity computation properties such as VelocityVectorProperty. const isValue = !defined(packetData.reference) && !defined(packetData.velocityReference); const 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); } let isSampled = false; if (isValue) { unwrappedInterval = unwrapInterval(type, packetData, sourceUri); if (!defined(unwrappedInterval)) { // not a known value type, bail return; } packedLength = type.packedLength ?? 1; unwrappedIntervalLength = unwrappedInterval.length ?? 1; 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. const needsUnpacking = typeof type.unpack === "function" && type !== Rotation; // 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, ); } else { object[propertyName] = createSpecializedProperty( type, entityCollection, packetData, ); } return; } let property = object[propertyName]; let epoch; const packetEpoch = packetData.epoch; if (defined(packetEpoch)) { epoch = JulianDate.fromIso8601(packetEpoch); } // 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)) { object[propertyName] = property = new SampledProperty(type); } property.addSamplesPackedArray(unwrappedInterval, epoch); updateInterpolationSettings(packetData, property); return; } let 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. combinedInterval = combinedInterval.clone(); if (isValue) { combinedInterval.data = needsUnpacking ? type.unpack(unwrappedInterval, 0) : unwrappedInterval; } else { combinedInterval.data = createSpecializedProperty( type, entityCollection, packetData, ); } // If no property exists, simply use a new interval collection if (!defined(property)) { object[propertyName] = property = isValue ? new TimeIntervalCollectionProperty() : new CompositeProperty(); } if (isValue && property instanceof TimeIntervalCollectionProperty) { // If we created a collection, or it already was one, use it. property.intervals.addInterval(combinedInterval); } else if (property instanceof CompositeProperty) { // 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. object[propertyName] = property = convertPropertyToComposite(property); // Change the new data to a ConstantProperty and add it. if (isValue) { combinedInterval.data = new ConstantProperty(combinedInterval.data); } property.intervals.addInterval(combinedInterval); } return; } // isSampled && hasInterval if (!defined(property)) { object[propertyName] = property = new CompositeProperty(); } // Create a CompositeProperty but preserve the existing data. if (!(property instanceof CompositeProperty)) { object[propertyName] = property = convertPropertyToComposite(property); } // Check if the interval already exists in the composite. const intervals = property.intervals; interval = intervals.findInterval(combinedInterval); if (!defined(interval) || !(interval.data instanceof SampledProperty)) { // If not, create a SampledProperty for it. interval = combinedInterval.clone(); interval.data = new SampledProperty(type); intervals.addInterval(interval); } interval.data.addSamplesPackedArray(unwrappedInterval, epoch); 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) { const intervals = property.intervals; for (let i = 0; i < intervals.length; ++i) { const 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; } if (Array.isArray(packetData)) { for (let i = 0, len = packetData.length; i < len; ++i) { processProperty( type, object, propertyName, packetData[i], interval, sourceUri, entityCollection, ); } } else { processProperty( type, object, propertyName, packetData, interval, sourceUri, entityCollection, ); } } function processPositionProperty( object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection, ) { let combinedInterval = intervalFromString(packetData.interval); if (defined(constrainedInterval)) { if (defined(combinedInterval)) { combinedInterval = TimeInterval.intersect( combinedInterval, constrainedInterval, scratchTimeInterval, ); } else { combinedInterval = constrainedInterval; } } const numberOfDerivatives = defined(packetData.cartesianVelocity) ? 1 : 0; const packedLength = Cartesian3.packedLength * (numberOfDerivatives + 1); let unwrappedInterval; let unwrappedIntervalLength; const isValue = !defined(packetData.reference); const 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); } let referenceFrame; let isSampled = false; if (isValue) { if (defined(packetData.referenceFrame)) { referenceFrame = ReferenceFrame[packetData.referenceFrame]; } referenceFrame = referenceFrame ?? ReferenceFrame.FIXED; unwrappedInterval = unwrapCartesianInterval(packetData); unwrappedIntervalLength = unwrappedInterval.length ?? 1; isSampled = unwrappedIntervalLength > packedLength; } // 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, ); } else { object[propertyName] = createReferenceProperty( entityCollection, packetData.reference, ); } return; } let property = object[propertyName]; let epoch; const packetEpoch = packetData.epoch; if (defined(packetEpoch)) { epoch = JulianDate.fromIso8601(packetEpoch); } // 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) ) { object[propertyName] = property = new SampledPositionProperty( referenceFrame, numberOfDerivatives, ); } property.addSamplesPackedArray(unwrappedInterval, epoch); updateInterpolationSettings(packetData, property); return; } let 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. combinedInterval = combinedInterval.clone(); if (isValue) { combinedInterval.data = Cartesian3.unpack(unwrappedInterval); } else { combinedInterval.data = createReferenceProperty( entityCollection, packetData.reference, ); } // If no property exists, simply use a new interval collection if (!defined(property)) { if (isValue) { property = new TimeIntervalCollectionPositionProperty(referenceFrame); } else { property = new CompositePositionProperty(referenceFrame); } object[propertyName] = property; } if ( isValue && property instanceof TimeIntervalCollectionPositionProperty && defined(referenceFrame) && property.referenceFrame === referenceFrame ) { // 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 (isValue) { combinedInterval.data = new ConstantPositionProperty( combinedInterval.data, referenceFrame, ); } property.intervals.addInterval(combinedInterval); } else { // Otherwise, create a CompositePositionProperty but preserve the existing data. object[propertyName] = property = convertPositionPropertyToComposite(property); // Change the new data to a ConstantPositionProperty and add it. if (isValue) { combinedInterval.data = new ConstantPositionProperty( combinedInterval.data, referenceFrame, ); } property.intervals.addInterval(combinedInterval); } return; } // isSampled && hasInterval if (!defined(property)) { object[propertyName] = property = new CompositePositionProperty( referenceFrame, ); } else if (!(property instanceof CompositePositionProperty)) { // Create a CompositeProperty but preserve the existing data. object[propertyName] = property = convertPositionPropertyToComposite(property); } // Check if the interval already exists in the composite. const intervals = property.intervals; interval = intervals.findInterval(combinedInterval); if ( !defined(interval) || !(interval.data instanceof SampledPositionProperty) || (defined(referenceFrame) && interval.data.referenceFrame !== referenceFrame) ) { // If not, create a SampledPositionProperty for it. interval = combinedInterval.clone(); interval.data = new SampledPositionProperty( referenceFrame, numberOfDerivatives, ); intervals.addInterval(interval); } interval.data.addSamplesPackedArray(unwrappedInterval, epoch); 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) { const intervals = property.intervals; for (let i = 0; i < intervals.length; ++i) { const 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; } if (Array.isArray(packetData)) { for (let i = 0, len = packetData.length; i < len; ++i) { processPositionProperty( object, propertyName, packetData[i], interval, sourceUri, entityCollection, ); } } else { processPositionProperty( object, propertyName, packetData, interval, sourceUri, entityCollection, ); } } function processShapePacketData( object, propertyName, packetData, entityCollection, ) { if (defined(packetData.references)) { processReferencesArrayPacketData( object, propertyName, packetData.references, packetData.interval, entityCollection, PropertyArray, CompositeProperty, ); } else { if (defined(packetData.cartesian2)) { packetData.array = Cartesian2.unpackArray(packetData.cartesian2); } else if (defined(packetData.cartesian)) { // for backwards compatibility, also accept `cartesian` packetData.array = Cartesian2.unpackArray(packetData.cartesian); } if (defined(packetData.array)) { processPacketData( Array, object, propertyName, packetData, undefined, undefined, entityCollection, ); } } } function processMaterialProperty( object, propertyName, packetData, constrainedInterval, sourceUri, entityCollection, ) { let combinedInterval = intervalFromString(packetData.interval); if (defined(constrainedInterval)) { if (defined(combinedInterval)) { combinedInterval = TimeInterval.intersect( combinedInterval, constrainedInterval, scratchTimeInterval, ); } else { combinedInterval = constrainedInterval; } } let property = object[propertyName]; let existingMaterial; let existingInterval; if (defined(combinedInterval)) { if (!(property instanceof CompositeMaterialProperty)) { property = new CompositeMaterialProperty(); object[propertyName] = property; } //See if we already have data at that interval. const thisIntervals = property.intervals; existingInterval = thisIntervals.findInterval({ start: combinedInterval.start, stop: combinedInterval.stop, }); if (defined(existingInterval)) { //We have an interval, but we need to make sure the //new data is the same type of material as the old data. existingMaterial = existingInterval.data; } else { //If not, create it. existingInterval = combinedInterval.clone(); thisIntervals.addInterval(existingInterval); } } else { existingMaterial = property; } let materialData; if (defined(packetData.solidColor)) { if (!(existingMaterial instanceof ColorMaterialProperty)) { existingMaterial = new ColorMaterialProperty(); } materialData = packetData.solidColor; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, undefined, entityCollection, ); } else if (defined(packetData.grid)) { if (!(existingMaterial instanceof GridMaterialProperty)) { existingMaterial = new GridMaterialProperty(); } materialData = packetData.grid; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, sourceUri, entityCollection, ); processPacketData( Number, existingMaterial, "cellAlpha", materialData.cellAlpha, undefined, sourceUri, entityCollection, ); processPacketData( Cartesian2, existingMaterial, "lineCount", materialData.lineCount, undefined, sourceUri, entityCollection, ); processPacketData( Cartesian2, existingMaterial, "lineThickness", materialData.lineThickness, undefined, sourceUri, entityCollection, ); processPacketData( Cartesian2, existingMaterial, "lineOffset", materialData.lineOffset, undefined, sourceUri, entityCollection, ); } else if (defined(packetData.image)) { if (!(existingMaterial instanceof ImageMaterialProperty)) { existingMaterial = new ImageMaterialProperty(); } materialData = packetData.image; processPacketData( Image, existingMaterial, "image", materialData.image, undefined, sourceUri, entityCollection, ); processPacketData( Cartesian2, existingMaterial, "repeat", materialData.repeat, undefined, sourceUri, entityCollection, ); processPacketData( Color, existingMaterial, "color", materialData.color, undefined, sourceUri, entityCollection, ); processPacketData( Boolean, existingMaterial, "transparent", materialData.transparent, undefined, sourceUri, entityCollection, ); } else if (defined(packetData.stripe)) { if (!(existingMaterial instanceof StripeMaterialProperty)) { existingMaterial = new StripeMaterialProperty(); } materialData = packetData.stripe; processPacketData( StripeOrientation, existingMaterial, "orientation", materialData.orientation, undefined, sourceUri, entityCollection, ); processPacketData( Color, existingMaterial, "evenColor", materialData.evenColor, undefined, sourceUri, entityCollection, ); processPacketData( Color, existingMaterial, "oddColor", materialData.oddColor, undefined, sourceUri, entityCollection, ); processPacketData( Number, existingMaterial, "offset", materialData.offset, undefined, sourceUri, entityCollection, ); processPacketData( Number, existingMaterial, "repeat", materialData.repeat, undefined, sourceUri, entityCollection, ); } else if (defined(packetData.polylineOutline)) { if (!(existingMaterial instanceof PolylineOutlineMaterialProperty)) { existingMaterial = new PolylineOutlineMaterialProperty(); } materialData = packetData.polylineOutline; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, sourceUri, entityCollection, ); processPacketData( Color, existingMaterial, "outlineColor", materialData.outlineColor, undefined, sourceUri, entityCollection, ); processPacketData( Number, existingMaterial, "outlineWidth", materialData.outlineWidth, undefined, sourceUri, entityCollection, ); } else if (defined(packetData.polylineGlow)) { if (!(existingMaterial instanceof PolylineGlowMaterialProperty)) { existingMaterial = new PolylineGlowMaterialProperty(); } materialData = packetData.polylineGlow; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, sourceUri, entityCollection, ); processPacketData( Number, existingMaterial, "glowPower", materialData.glowPower, undefined, sourceUri, entityCollection, ); processPacketData( Number, existingMaterial, "taperPower", materialData.taperPower, undefined, sourceUri, entityCollection, ); } else if (defined(packetData.polylineArrow)) { if (!(existingMaterial instanceof PolylineArrowMaterialProperty)) { existingMaterial = new PolylineArrowMaterialProperty(); } materialData = packetData.polylineArrow; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, undefined, entityCollection, ); } else if (defined(packetData.polylineDash)) { if (!(existingMaterial instanceof PolylineDashMaterialProperty)) { existingMaterial = new PolylineDashMaterialProperty(); } materialData = packetData.polylineDash; processPacketData( Color, existingMaterial, "color", materialData.color, undefined, undefined, entityCollection, ); processPacketData( Color, existingMaterial, "gapColor", materialData.gapColor, undefined, undefined, entityCollection, ); processPacketData( Number, existingMaterial, "dashLength", materialData.dashLength, undefined, sourceUri, entityCollection, ); processPacketData( Number, existingMaterial, "dashPattern", materialData.dashPattern, undefined, sourceUri, entityCollection, ); } else if (defined(packetData.checkerboard)) { if (!(existingMaterial instanceof CheckerboardMaterialProperty)) { existingMaterial = new CheckerboardMaterialProperty(); } materialData = packetData.checkerboard; processPacketData( Color, existingMaterial, "evenColor", materialData.evenColor, undefined, sourceUri, entityCollection, ); processPacketData( Color, existingMaterial, "oddColor", materialData.oddColor, undefined, sourceUri, entityCollection, ); processPacketData( Cartesian2, existingMaterial, "repeat", materialData.repeat, undefined, sourceUri, entityCollection, ); } if (defined(existingInterval)) { existingInterval.data = existingMaterial; } else { object[propertyName] = existingMaterial; } } function processMaterialPacketData( object, propertyName, packetData, interval, sourceUri, entityCollection, ) { if (!defined(packetData)) { return; } if (Array.isArray(packetData)) { for (let i = 0, len = packetData.length; i < len; ++i) { processMaterialProperty( object, propertyName, packetData[i], interval, sourceUri, entityCollection, ); } } else { processMaterialProperty( object, propertyName, packetData, interval, sourceUri, entityCollection, ); } } function processName(entity, packet, entityCollection, sourceUri) { const nameData = packet.name; if (defined(nameData)) { entity.name = packet.name; } } function processDescription(entity, packet, entityCollection, sourceUri) { const descriptionData = packet.description; if (defined(descriptionData)) { processPacketData( String, entity, "description", descriptionData, undefined, sourceUri, entityCollection, ); } } function processPosition(entity, packet, entityCollection, sourceUri) { const positionData = packet.position; if (defined(positionData)) { processPositionPacketData( entity, "position", positionData, undefined, sourceUri, entityCollection, ); } } function processViewFrom(entity, packet, entityCollection, sourceUri) { const viewFromData = packet.viewFrom; if (defined(viewFromData)) { processPacketData( Cartesian3, entity, "viewFrom", viewFromData, undefined, sourceUri, entityCollection, ); } } function processOrientation(entity, pa