The best and most efficient implementation of dynamic forms in cesium

I am currently working on an application using the Cesium Viewer. I need to be able to display a collection of forms that will be dynamically updated. I am having trouble understanding the best way to do this.

I am currently using Entities and using CallbackProperties to allow shape updates.

You can do it in a sand castle to understand how I do it. There is a polygon object that is used as the basis for cesiumCallback, and it is edited by another piece of code. (simulated using setTimeout)

var viewer = new Cesium.Viewer('cesiumContainer', {}); var polygon = {}; polygon.coordinates = [ {longitude: 0, latitude: 0, altitude: 0}, {longitude: 10, latitude: 10, altitude: 0}, {longitude: 10, latitude: 0, altitude: 0} ]; // converts generic style options to cesium one (aka color -> material) var polOpts = {}; // function for getting location polOpts.hierarchy = new Cesium.CallbackProperty(function() { var hierarchy = []; for (var i = 0; i < polygon.coordinates.length; i++) { var coordinate = polygon.coordinates[i]; hierarchy.push(Cesium.Cartesian3.fromDegrees(coordinate.longitude, coordinate.latitude, coordinate.altitude)); } return hierarchy; }, false); viewer.entities.add({polygon: polOpts}); setInterval(function(polygon){ polygon.coordinates[0].longitude--; }.bind(this, polygon), 1000); 

The resulting polygon is a class that generally describes the polygon, so it has an array of coordinates and style parameters, as well as a rendering method that calls this renderPolygon method, which runs on its own. This shape rendering method works for everything I need, but it is not very efficient. There are two cases of updating shapes; one type of shape will be updated over a long period of time, like a slow speed, just in a few seconds. Another is forms that will be updated many times, for example thousands, in a few seconds, and then will not change again for a long time, if ever.

I had two ideas on how to fix this.

Idea 1: There are two methods: renderDynamicPolygon and renderStaticPolygon. The renderDynamicPolygon method would perform the above functions using cesiumCallbackProperties. This will be used for forms that are updated multiple times in a short time when they are updated. The renderStaticPolygon method replaces the properties of entities that will use callbackProperties with constant values โ€‹โ€‹as soon as the update is completed.

This creates a lot of other work to make sure that the shapes are in the correct state and do not help slowly changing shapes over a long period of time.

Idea 2: Similar to how primitives work, I tried to delete the old object and add it again with updated properties every time I needed to update it, but this led to flickering and, unlike primitives, I could not find the async property for entities.

I also tried using primitives. It is perfect for polylines, I just delete the old one and add a new one with updated properties. I also used async = false so that there is no flicker. This problem that I ran into was not to create all forms using primitives. (It's true?)

Another thing I tried is to use a geometry instance using geometry and appearance. After going through the tutorial on the cesium website, I was able to display several shapes and could update the look, but found it was almost impossible to figure out how to properly update the shapes, and it was also very difficult to get them before Look correctly. Forms must have the correct shape, fill color and opacity and stroke color, opacity and weight. I tried using polygonOutlineGeometry but no luck.

What would be the best way to implement this? Is one of these options the right direction, or is there some other way to do this, I have not yet revealed?


[Edit] I added the answer where I received, but still not complete and looking for answers.

+6
source share
2 answers

I came up with a pretty good solution for this, but it still has one small problem.

I also used ways to display objects. I call one render and one paint. Render uses Cesium.CallbackProperty with the isConstant true property and draws with isConstantProperty false .

Then I created a function to change the entity from rendering to paint and vice vera. It goes through the properties of the entity callback, uses the setCallback property to overwrite the property with the correct function and isConstant value.

Example: I create an ellipse based on the circle object that I defined.

 // isConst is True if it is being "painted" and false if it is being "rendered" ellipse: lenz.util.extend(this._getStyleOptions(circle), { semiMinorAxis: new Cesium.CallbackProperty( this._getRadius.bind(this, circle), isConst ), semiMajorAxis: new Cesium.CallbackProperty( this._getRadius.bind(this, circle), isConst ), }) 

So, when the form is updated (while the user is drawing the form), the form is rendered using isConstant, which is false. Then, when the drawing is completed, it is converted to a colored version using the following code:

 existingEntity.ellipse.semiMinorAxis.setCallback( this._getRadius.bind(this, circle), isConst ); existingEntity.ellipse.semiMajorAxis.setCallback( this._getRadius.bind(this, circle, 1), isConst ); 

It works great. I can draw hundreds of shapes without a drop. I added screenshots of the cesium map with 612 objects before and after my changes, the frame rate is in the upper right corner using the chrome rendering tool.

Before: Blocked at fps 0.9 Note: I edited the rest of the ui, the witch makes the globe look cut off, sorry enter image description here

And after the changes: fps remains at 59.9, almost perfect!

enter image description here

Whenever an entity is "converted" from using constants to constant properties of the callback, it and all other objects of the same type flash again and turn on again. I cannot find a better way to do this conversion. I feel that I still need to lose something.

+1
source

You can try using PositionPropertyArray as a hierarchy of polygons with SampledPositionProperty for any dynamic positions and ConstantPositionProperty for any static positions. I'm not sure if it will work better than your decision, but it might be worth checking out. Here's an example of how this might work that you can insert into a Cesium Sandcastle:

 var viewer = new Cesium.Viewer('cesiumContainer', {}); // required if you want no interpolation of position between times var noInterpolation = { type: 'No Interpolation', getRequiredDataPoints: function (degree) { return 2; }, interpolateOrderZero: function (x, xTable, yTable, yStride, result) { if (!Cesium.defined(result)) { result = new Array(yStride); } for (var i = 0; i < yStride; i++) { result[i] = yTable[i]; } return result; } }; var start = viewer.clock.currentTime; // set up the sampled position property var sampledPositionProperty = new Cesium.SampledPositionProperty(); sampledPositionProperty.forwardExtrapolationType = Cesium.ExtrapolationType.HOLD; sampledPositionProperty.addSample(start, new Cesium.Cartesian3.fromDegrees(0, 0)); // initial position sampledPositionProperty.setInterpolationOptions({ interpolationAlgorithm: noInterpolation }); // set up the sampled position property array var positions = [ sampledPositionProperty, new Cesium.ConstantPositionProperty(new Cesium.Cartesian3.fromDegrees(10, 10)), new Cesium.ConstantPositionProperty(new Cesium.Cartesian3.fromDegrees(10, 0)) ]; // add the polygon to Cesium viewer var polygonEntity = new Cesium.Entity({ polygon: { hierarchy: new Cesium.PositionPropertyArray(positions) } }); viewer.zoomTo(viewer.entities.add(polygonEntity)); // add a sample every second var counter = 1; setInterval(function(positionArray) { var time = new Cesium.JulianDate.addSeconds(start, counter, new Cesium.JulianDate()); var position = new Cesium.Cartesian3.fromDegrees(-counter, 0); positionArray[0].addSample(time, position); counter++; }.bind(this, positions), 1000); 

One good thing about this is that you can set the start / end time of the timeline to a reasonable range and use it to view your polygon at any time within the selection range so you can see the history of your polygons in time (see here on how to change the start / end time of the timeline). In addition, you do not need to use timers to set positions, time is built into SampledPositionProperty (although you can add asynchronous samples).

However, this also means that the position depends on the current time on the timeline instead of the array value in real time. And you may need to track some time if you do not add all samples at once.

I also have never done this using ellipses before, but semiMinorAxis and semiMajorAxis are properties, so you can still use SampledProperty .

Of course, this does not really matter if there are performance issues. I hope this improves, since you donโ€™t need to recreate the array from scratch every callback, and depending on how you get the data to update the polygons, you can add several samples at once. This is just an assumption, but it is something to consider.

EDIT

Cesium can process quite a few samples added to a selective position, for example, in the code above, if you add a million samples to a position that takes a few seconds to load them all, but makes a polygon at any time without any performance problems. To test this, instead of adding samples using a timer, just add them all directly to the property.

 for (var i = 0; i < 1000000; i++) { var time = new Cesium.JulianDate.addSeconds(start, i, new Cesium.JulianDate()); var position = new Cesium.Cartesian3.fromDegrees(-(i % 2), 0); positions[0].addSample(time, position); } 

However, if you encounter memory problems, there is currently no way to remove patterns from the position property without accessing private variables . A workaround would be to periodically create a new array containing the new position properties and use the previous setValue() property array method to clear the previous values โ€‹โ€‹or perhaps use TimeIntervalCollectionProperty as in this answer and remove the time intervals using the removeInterval method.

0
source

All Articles