Visualization with Cesium |
Cesium is an open-source JavaScript library, founded by AGI in 2011. Cesium provides a cross-platform, browser-based virtual globe with a focus on precision and time-dynamic data. DME Component Libraries also includes an enhanced commercial version of Cesium, Cesium Analytics SDK.
In DME Component Libraries, the Cesium Library provides the ability to generate CZML content from Platforms and other DefinitionalObjects, as well as analysis derived from them. CZML is a JSON-based data format designed by AGI to load static and time-dynamic data into Cesium.
This topic assumes basic familiarity with Cesium. Cesium documentation, tutorials, and code samples are available separately from the Cesium site.
The Cesium Library is built upon the same design patterns used throughout DME Component Libraries. It provides ObjectExtensions that describe how CZML properties are defined over time. These extensions are then added to individual Platforms and other ExtensibleObjects. Effectively, these extensions add CZML visualization information to your existing objects, after which they can be added to a CzmlDocument and serialized to CZML.
To understand how this works in practice, the following code sample demonstrates how to draw a label at the location of the International Space Station, and draw its orbit.
EarthCentralBody earth = CentralBodiesFacet.getFromContext().getEarth(); TwoLineElementSet issTle = new TwoLineElementSet("1 25544U 98067A 10172.34241898 .00007451 00000-0 60420-4 0 3627\r\n" + "2 25544 51.6459 209.3399 0009135 352.3227 186.5240 15.71934500664129"); Point issPoint = new Sgp4Propagator(issTle).createPoint(); Platform iss = new Platform(); iss.setName("ISS"); iss.setLocationPoint(issPoint); iss.setOrientationAxes(new AxesVehicleVelocityLocalHorizontal(earth.getFixedFrame(), issPoint)); LabelGraphics issLabel = new LabelGraphics(); issLabel.setText(new ConstantCesiumProperty<>(iss.getName())); issLabel.setFillColor(new ConstantCesiumProperty<>(Color.WHITE)); LabelGraphicsExtension labelExtension = new LabelGraphicsExtension(issLabel); iss.getExtensions().add(labelExtension); PolylineOutlineMaterialGraphics issPathMaterial = new PolylineOutlineMaterialGraphics(); issPathMaterial.setColor(new ConstantCesiumProperty<>(Color.WHITE)); issPathMaterial.setOutlineWidth(new ConstantCesiumProperty<>(1.0)); issPathMaterial.setOutlineColor(new ConstantCesiumProperty<>(Color.BLACK)); PathGraphics issPath = new PathGraphics(); issPath.setMaterial(new ConstantCesiumProperty<>(issPathMaterial)); issPath.setWidth(new ConstantCesiumProperty<>(2.0)); issPath.setLeadTime(new ConstantCesiumProperty<>(Duration.fromMinutes(44.0).getTotalSeconds())); issPath.setTrailTime(new ConstantCesiumProperty<>(Duration.fromMinutes(44.0).getTotalSeconds())); iss.getExtensions().add(new PathGraphicsExtension(issPath)); TimeInterval interval = new TimeInterval(new JulianDate(new GregorianDate(2009, 1, 1)), new JulianDate(new GregorianDate(2009, 1, 2))); CzmlDocument czmlDocument = new CzmlDocument(); czmlDocument.setName("SimpleExample"); czmlDocument.setDescription("A simple example"); czmlDocument.setPrettyFormatting(true); czmlDocument.setRequestedInterval(interval); Clock clock = new Clock(); clock.setInterval(interval); clock.setCurrentTime(interval.getStart()); czmlDocument.setClock(clock); czmlDocument.getObjectsToWrite().add(iss); try (Writer writer = Files.newBufferedWriter(outputPath.resolve("Example.czml"))) { czmlDocument.writeDocument(writer); }
First, we create a Platform to represent the ISS, with its location determined by a TLE. We then add a LabelGraphicsExtension and a PathGraphicsExtension. These extensions define various graphical properties that determine how the object will be displayed in Cesium. Then, to write the CZML, we create a CzmlDocument, provide a name and description for the document, configure the time interval over which we want to write data for, add the ISS platform to the list of objects to write, and then finally write the CZML file to disk. If you were to load this file into Cesium, you would see something like the below:
In the code above, we configure the graphical properties using instances of ConstantCesiumProperty<T>. In CZML, almost every value can be defined to vary over time, so graphical properties, such as FillColor (get / set), are actually instances of the abstract base class CesiumProperty<T>. ConstantCesiumProperty<T> is merely the simplest implementation, for cases where a value is constant with respect to time.
CesiumProperty<T> is a means to define how a particular graphical property of an object in your Cesium scene changes over time. This allows you to associate the results of your analysis with the desired appearance of the display and is a fundamental concept in the Cesium Library.
The following additional types of CesiumProperty<T> are available:
TimeIntervalCesiumProperty<T> - A property that has different values over various intervals. The value will change instantaneously at the boundaries of the time intervals. These properties will be written using CZML interval syntax. The data is stored in a TimeIntervalCollection1<T>.
SampledCesiumProperty<T> - A property that changes over time by interpolating on the client over time-tagged samples. The data is stored in a DateMotionCollection1<T>.
AccessQueryCesiumProperty<T> - A property that changes its value based on the results of an AccessQuery. These properties will be written using CZML interval syntax.
CompositeCesiumProperty<T> - A property that defines its value differently over different intervals. This property associates other properties with time intervals. Data is stored in a TimeIntervalCollection1<CesiumProperty<T>>.
ScalarCesiumProperty - A property that adapts any Scalar to be used as a property of type double.
AxesCesiumProperty - A property that adapts any Axes to be used as a property of type UnitQuaternion.
PointCesiumProperty - A property that adapts any Point to be used as a property of type Cartesian.
VectorCesiumProperty - A property that adapts any Vector to be used as a property of type Cartesian.
UnitVectorCesiumProperty - A property that adapts any normalized Vector to be used as a property of type UnitCartesian.
VelocityVectorDirectionCesiumProperty - A property which indicates that a UnitCartesian property will be computed on the client to be the direction of the velocity vector of a particular object. For example, a BillboardGraphics.AlignedAxis (get / set) can be configured to be the velocity vector.
DeleteCesiumProperty<T> - A property which indicates that existing data for a property should be deleted from the client, optionally over a TimeInterval.
As an example, suppose we wanted the text of the ISS label to be green before January 1st, 2009, and red any time after that. The following example shows how to accomplish this using a TimeIntervalCesiumProperty<Color>:
JulianDate colorChangeTime = new JulianDate(new GregorianDate(2009, 1, 1)); TimeIntervalCollection1<Color> intervals = new TimeIntervalCollection1<>(); Color green = new Color(0x008000); intervals.add(new TimeInterval1<>(JulianDate.getMinValue(), colorChangeTime, green)); intervals.add(new TimeInterval1<>(colorChangeTime, JulianDate.getMaxValue(), Color.RED)); labelExtension.getLabelGraphics().setFillColor(new TimeIntervalCesiumProperty<>(intervals));
By default, the identifiers for each CZML object will be auto-generated, but if you need more control, you can add an IdentifierExtension to configure the identifier for that object.
iss.getExtensions().add(new IdentifierExtension("ISS"));
So far we have mainly been using color, but the same model applies to all other CZML properties. Suppose we wanted to visualize when the ISS had line of sight to a point on the ground, for example, AGI headquarters. In DME Component Libraries, we model this as an Access problem, using LinkInstantaneous and a CentralBodyObstructionConstraint.
Cartographic facilityLocation = new Cartographic(-1.3191780323141054, 0.69871349190638687, 0.0); Point facilityPoint = new PointCartographic(earth, facilityLocation); Platform facility = new Platform(); facility.setName("AGI HQ"); facility.setLocationPoint(facilityPoint); facility.setOrientationAxes(new AxesEastNorthUp(earth, facilityPoint)); LabelGraphics facilityLabel = new LabelGraphics(); // Note: toCesiumProperty is a static import of CesiumProperty.toCesiumProperty facilityLabel.setText(toCesiumProperty(facility.getName())); facilityLabel.setFillColor(toCesiumProperty(Color.WHITE)); facility.getExtensions().add(new LabelGraphicsExtension(facilityLabel)); LinkInstantaneous link = new LinkInstantaneous(facility, iss); CentralBodyObstructionConstraint constraint = new CentralBodyObstructionConstraint(link, earth);
We can then use these analysis types directly to visualize the results in Cesium. First, we create an LinkGraphicsExtension to describe the graphical properties of our link, which will be represented using a polyline in CZML. In our example, we make the link yellow by configuring the link's material. See the next section for more information on materials. Next, we want to only show the link when the constraint is satisfied. Since CentralBodyObstructionConstraint is an AccessQuery, we define the Show (get / set) property as an AccessQueryCesiumProperty<Boolean>, indicating that the link graphics should be shown when access exists, and not otherwise. The following code sample shows this:
AccessQueryCesiumProperty<Boolean> showProperty = new AccessQueryCesiumProperty<>(); showProperty.setQuery(constraint); showProperty.setAccessExists(true); showProperty.setAccessUnknown(false); showProperty.setNoAccess(false); LinkGraphics linkGraphics = new LinkGraphics(); linkGraphics.setShow(showProperty); linkGraphics.setMaterial(toCesiumProperty(new SolidColorMaterialGraphics(Color.YELLOW))); link.getExtensions().add(new LinkGraphicsExtension(linkGraphics));
In the above examples, we configured both PathGraphics and LinkGraphics using materials. A material defines how the surface of many Cesium graphical types will be rendered. For example, Cesium (and therefore CZML) defines a PolylineOutline material which renders an outlined polyline. In DME Component Libraries, this corresponds to the PolylineOutlineMaterialGraphics type. Alternatively, we could use a SolidColorMaterialGraphics, or any other material instead. Each material defines its own set of specialized graphical properties. For example, the PolylineGlowMaterialGraphics provides Color (get / set) and GlowPower (get / set) properties.
In addition to polyline material types, which implement the IPolylineMaterialGraphics interface, there are also more general material types, which implement the IMaterialGraphics interface. These materials are used for shape and volume rendering, such as CentralBodySurfaceRegionGraphics and SensorFieldOfViewGraphics. In fact, sensor visualization allows different materials to be used for different parts of the sensor volume. For example, the following code sample adds a sensor to our facility representing AGI, pointing at the ISS as it passes overhead. We make the primary sensor volume green, but we use a white grid material to represent the sensor dome.
VectorFixed referenceVector = new VectorFixed(iss.getOrientationAxes(), Cartesian.toCartesian(UnitCartesian.getUnitZ())); Axes sensorOrientation = new AxesTargetingLink(link, LinkRole.TRANSMITTER, referenceVector); Platform sensor = new Platform(); sensor.setLocationPoint(facility.getLocationPoint()); sensor.setOrientationAxes(sensorOrientation); RectangularPyramid rectangularPyramid = new RectangularPyramid(); rectangularPyramid.setXHalfAngle(Trig.degreesToRadians(8.0)); rectangularPyramid.setYHalfAngle(Trig.degreesToRadians(4.5)); rectangularPyramid.setRadius(500000.0); sensor.getExtensions().add(new FieldOfViewExtension(rectangularPyramid)); SensorFieldOfViewGraphics fieldOfViewGraphics = new SensorFieldOfViewGraphics(); GridMaterialGraphics gridMaterial = new GridMaterialGraphics(); gridMaterial.setColor(toCesiumProperty(Color.WHITE)); gridMaterial.setCellAlpha(toCesiumProperty(0.0)); fieldOfViewGraphics.setDomeSurfaceMaterial(toCesiumProperty(gridMaterial)); Color transparentGreen = new Color(0x80008000, true); SolidColorMaterialGraphics lateralSurfaceMaterial = new SolidColorMaterialGraphics(transparentGreen); fieldOfViewGraphics.setLateralSurfaceMaterial(toCesiumProperty(lateralSurfaceMaterial)); sensor.getExtensions().add(new FieldOfViewGraphicsExtension(fieldOfViewGraphics));
When loaded in Cesium Analytics SDK, this looks like:
The Code Sample topic puts each of the pieces described above into one complete example.
In advanced situations, you may need to delete data from a Cesium client via CZML documents processed into an existing client-side data source. This can also be necessary in order to replace data correctly, because processing data is an additive operation. As a result, by default, sampled properties have their samples merged with existing samples, which can produce incorrect visualization.
In order to delete data, objects must have stable identifiers. See the Identifiers section for more information on associating objects with identifiers.
There are several different mechanisms for deleting data:
CesiumDeleteExtension can be used to delete an entire object from the client. The object can then be recreated from scratch in a subsequent packet.
DeleteCesiumProperty<T> can be used to selectively delete data for a single property without affecting any other data. By default, all data will be deleted, or a time interval can be specified to only delete data that falls within that interval. This can be used to prune old data to reduce memory usage on the client.
Some CZML data is not written using CesiumProperty<T> objects. For example, position data is generated using the more general ILocationPointService mechanism. In these cases, specialized extensions can be added to the object to indicate that data from these properties should be deleted, instead of being generated as normal. These extensions are:
Each extension also allows specifying an interval, similar to DeleteCesiumProperty<T>.
The recommended approach to deleting an object is to use a placeholder ExtensibleObject, which has no extensions by default, then specify the identifier of the existing object, and add the extensions to delete the desired data, then add the placeholder object to the CzmlDocument.
Because CZML is generated for objects in order, you can then add a complete definition of the object using the same identifier. The generated CZML will contain a packet deleting the requested data, then a subsequent packet adding new data. Because Cesium processes packets in order, this will effectively replace the data.
The functionality described above is designed to produce a complete description of a set of objects to be visualized in Cesium. In most use cases, this conceptually simple way of transmitting information is sufficient, even in situations where objects may change their definition. A complete CzmlDocument can be written each time definitions change, and loaded into Cesium, replacing the previous document on the client. Since each document fully describes all objects, they are independent from any previous state.
In advanced situations with large amounts of data, producing complete documents can result in a large amount of CZML to be transmitted and processed. In these cases, update documents can be generated to send only data that has changed, which requires a more complex implementation but can significantly reduce the amount of data produced.
CzmlClientEntitySet represents the complete state of all entities for a specific Cesium client. Each client should have a separate instance associated with it, and the entity set should persist as long as the client is active.
Next, whenever the state of the objects change, create a complete CzmlDocument containing all objects, including all extensions, that should be visualized. Make sure that all objects have stable identifiers (see the Identifiers section) because identifiers will be used to correlate objects across updates.
Then, call add to add the new complete document to the entity set. This method returns a CzmlUpdateDocument which contains the changes between the previous state and the new document. This update document can be then written to the client and processed (without clearing existing data).
This technique is most useful when building a web application that uses Cesium as the front-end, with DME Component Libraries providing server-side analysis, to provide a common operational picture. A Cesium-based client could refresh its data on a schedule, or the server could push updates to clients when they occur. An entity set would be maintained associated with each Cesium client, and the update documents would then be written directly to the web service response to be processed by the client.
The following code sample shows the intended sequence of operations to create update documents for a single client. The details of the CZML document are omitted here.
// Create an entity set for a specific Cesium client. CzmlClientEntitySet entitySet = new CzmlClientEntitySet(); // Create a complete document containing all entities and all extensions. CzmlDocument czmlDocument = createDocument(); // Add the document to the entity set, producing a CzmlUpdateDocument. CzmlUpdateDocument updateDocument = entitySet.add(czmlDocument); // Write the update document to a writer, such as a web service response. updateDocument.writeDocument(getOutputWriter()); // Later... // Create a new complete document containing all entities. // Entities will be correlated by identifier. czmlDocument = createDocument(); // Add the new complete document to the existing entity set. // The update document will contain only the data that has changed // since the previous document was added. updateDocument = entitySet.add(czmlDocument); // Write the new update document as before. updateDocument.writeDocument(getOutputWriter());
The table below provides a list of all services and related extensions involved in writing CZML. While most generated CZML is supported by open-source Cesium, some features are only supported by Cesium Analytics SDK. Loading a CZML file with these features into open-source Cesium will simply cause the unsupported features to be ignored.
Service | Extension | Description | CZML Type | Availability |
---|---|---|---|---|
Visualizes an AzimuthElevationMask at the object's position, oriented using the object's orientation. The geometry will be defined by the IAzimuthElevationMaskService service. | agi_fan | Cesium Analytics SDK | ||
Visualizes a 2D marker at the object's position. | billboard | Cesium | ||
Visualizes a surface curve on an central body. The geometry of the curve will be defined by the ICentralBodySurfaceCurveService service. | polyline | Cesium | ||
Visualizes the outline, interior and boundary wall of a surface region on an central body. The geometry of the region will be defined by the ICentralBodySurfaceRegionService service. | polygon | Cesium | ||
Overrides the auto-detected availability of a CZML object with the specified interval. | availability | Cesium | ||
Defines custom time-varying CZML properties for use by client-side code. | properties | Cesium | ||
Indicates that a CZML object with the associated identifier should be deleted from the client. | delete | Cesium | ||
Customizes details about how an vector's direction will be written to CZML. | orientation | Cesium | ||
Customizes the step size to use when sampling properties for an object, instead of using the value specified for CzmlDocument.Step (get / set). | Cesium | |||
Customizes details about how an object's orientation will be written to CZML. | orientation | Cesium | ||
Customizes details about how an object's position will be written to CZML. | position | Cesium | ||
Allows a CZML object's position to be written in the inertial frame. | position | Cesium | ||
Specifies the HTML description of an object. | description | Cesium | ||
Visualizes a 3D ellipsoid at the object's position, oriented using the object's orientation. | ellipsoid | Cesium | ||
Defines graphical properties for sensor volumes and footprints projected onto a central body, at the object's position, oriented using the object's orientation. The geometry of the sensor volume will be defined by the IFieldOfViewService service. | agi_conicSensor, agi_customPatternSensor, agi_rectangularSensor | Cesium Analytics SDK | ||
Specifies the identifier of the object, which must be unique. If not provided, a unique identifier will be generated automatically. | id | Cesium | ||
Visualizes text at the object's position. | label | Cesium | ||
Visualizes a polyline drawn connecting the two end points of a link. The geometry of the polyline will be defined by the ILinkService service. | polyline | Cesium | ||
Specifies the position for an object. Most graphical extensions require this information. | position | Cesium | ||
Visualizes a 3D model in glTF format at the object's position, oriented using the object's orientation. See the glTF repository for more details on the glTF format. | model | Cesium | ||
The name of the object, which does not have to be unique and is intended for user consumption. | name | Cesium | ||
Specifies the orientation of an object over time. Some 3D graphical extensions require this information. | orientation | Cesium | ||
The parent object of the object. | parent | Cesium | ||
A path of an object as it moves over time. | path | Cesium | ||
Visualizes a 2D point at the object's position. | point | Cesium | ||
Visualizes a 3D tileset at the object's position, oriented using the object's orientation. | tileset | Cesium | ||
Defines the graphical properties of a 3D Vector, at the object's position. The geometry of the vector is defined by the IVectorService service. | agi_vector | Cesium Analytics SDK | ||
Specifies the suggested initial camera view offset when tracking an object. | viewFrom | Cesium |