@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
JavaScript
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