Cesium in 2015 - Entity API

Of all the items in the Cesium in 2015 - Kickoff post, the one that may have been the most surprising to many of you is the “High-level Entity API”. This post should get everyone up to speed and on the same page. Even if you have worked with entities in their current form, huge improvements are coming, so keep reading.

Cesium has always been a tale of two APIs. On the one hand we have low-level primitives; which includes items like BillboardCollection, Model, Geometry, Appearances, Materials, etc… These primitives make up the brunt of the Cesium graphics API and are incredibly powerful. The only problem with them is that they are targeted more at computer graphics developers than application developers. It doesn’t sugar coat a lot for you and having to deal with Primitive batching, model matrices, uniforms, per-instance attributes, etc… can be obtuse and intimidating if you haven’t had previous experience with them. Furthermore, these aspects of Cesium don’t have any direct support for time; it’s up to the user to update values every frame, which can makes easy to write non-performant code. None of this is a bad; this API is an essential part of Cesium that enabled everything else; it’s just lower level than what many users are used to dealing with.

The other API is the Entity API. It was originally written exclusively for CZML but was eventually refactored (several times) to become the basis for how we load all standard data sources, such as CZML, GeoJSON, TopoJSON, and soon KML. That’s why internally it’s referred to as the DataSources layer. The API is well structured and incredibly featureful and we don’t need to know anything about computer graphics to use it. The main problem with it is that it’s target audience is hard core software developers who are trying to deal with complex, large-scale, time-dynamic data. The barrier to entry is too high and it involves creating our own class and implementing the DataSource interface before we can even get started. It’s not conducive to learning by experimentation via Sandcastle like other parts of Cesium are.

Over course of the last year, we’ve had several conversations about how to make Cesium easier to use and more approachable for everyone. We even floated (briefly) the idea of creating a third API point, but decided that would be ridiculous. Instead, we realized that the Entity API has slowly matured to the point where it was already at about 80% of where we wanted to go and if we spent some time on making it easy and approachable it would be exactly what we need.

Before I make existing Cesium users too nervous, don’t worry. The lower level API won’t be going away; but it will be deemphasized in high-level documentation and Sandcastle. We’re in the process of writing several new tutorials and on-ramps for Cesium and they all revolve around entities. The API will also have customization points when the high-level API can’t do something out of the box (for example, if you need to implement a custom material, we’ll probably support that). If you’re truly doing custom stuff, primitives will still be there to use and you can mix and match entities and primitives as you see fit.

For those of you with existing Entity code, everything we’re doing is backwards compatible, so you won’t have to make any changes to your code. (We have made doing some things much cleaner, so you may end up wanting to anyway). We may deprecate some things over time, but the transition will be smooth and painless.

Okay, so I should probably stop babbling and show you some code. What is the entity API? Let’s start by taking a look at the two “old-way” Sandcastle examples currently on the website: Colors Ellipses and Ellipse outlines.

These examples are similar, but combining them into one doesn’t save you much code because outlines and interiors are treated independently in the geometry & appearances architecture (because that’s how low-level graphics work). Ignoring comments, it’s about 100 lines of code combined and it doesn’t do anything other than draw some ellipses. If you’re new to Cesium and not a graphics developer, they probably look pretty complicated (I know they were for me when I first learned them).

Now look at this example, which is a preview of the entity changes: Circles and Ellipses with Entity API

This is basically the first two examples combined, it has both outlines and filled ellipses. The first thing you might notice is that it’s only 42 lines of code. On top of that, the code is much easier to read. There’s no separate ellipse and ellipse outline, no manual batching, no primitives, vertex formats, or render states. Everything is handled for you under the hood; you just give us the data and it shows up on the globe. You could probably figure out yourself how to do something like change the color of an ellipse from red to yellow on your first try (hint it’s redEllipse.ellipse.material = Cesium.Color.YELLOW). Let’s say instead you wanted to texture it with an image, redEllipse.ellipse.material = ‘image.png’;

If you’ve used CZML or GeoJSON, you may have noticed there is no DataSource directly involved. We can simply create entities and add them to the viewer.entities collection. On top of that there’s no need to construct an Entity, EllipseGraphics, or any ConstantProperty instances (though you can if you want it). Again, this is all going on behind the scenes for you.

Instead of having to manage groups of primitives yourself; Entity is your one stop shop for everything. For example, if you wanted to add a label to the redEllipse after you constructed it:

redEllipse.label = {

text: ‘My label!’,

font: ‘32pt sans-serif’

};

What about an HTML description for the InfoBox?

redEllipse.description = ‘I’m an HTML description.’;

It doesn’t matter if the user clicks on the label, the ellipse, or it’s outline. All of these items are aggregated together as an Entity, so the description will show up in the InfoBox. You could add a model, billboard, or other shapes in there as well. You can also zoom to the entity, which isn’t automatically handled by the low-level geometry.

The above example uses the Entity API at it’s highest level, but it just scratches the surface of what you can do. For example, if you wanted to make your ellipse red before January 14th, 2015, but then slowly transition to Green until January 15th, 2015 (and stay green the rest of the time:

var color = new Cesium.SampledProperty(Cesium.Color);

color.addSample(Cesium.JulianDate.fromIso8601(‘2015-01-14T00Z’), Cesium.Color.RED);

color.addSample(Cesium.JulianDate.fromIso8601(‘2015-01-15T00Z’), Cesium.Color.GREEN);

redEllipse.ellipse.material = new Cesium.ColorMaterialProperty(color);

Paste this code at the bottom of the example and try it out. Now the color of our ellipse will be red up until midnight on Jan 14 at which point it will slowly change to green over the course of the day. You don’t have to do any of the bookkeeping yourself, it’s all handled for you.

Every property we’ve shown you so far can also be made time-dynamic. In the above we are using a SampledProperty, but there are also other kinds. I won’t go into them now but they’ll be a full tutorial on them soon. One thing that’s important to note right away is that when you assign a value like label.text = ‘something’, this is actually shorthand for label.text = new Cesium.ConstantProperty(‘something’); So in both cases if you read back label.text, you get an instance of ConstantProperty.

Entity Tracking and Zooming

Camera control is another thing we wanted to make easier when working with entities. I’m sure some of you have used the viewer.trackedEntity property. The two major drawbacks of it are that it’s hard to have a good default view (because we require the user manually assign entity.viewFrom and tell us where the camera should be) and it doesn’t work unless entity.position is set (which many shapes, like polygons, don’t normally have). We are in the process of updating viewer.trackedEntity so that it provides excellent default views in most cases with no extra data from the user. We don’t have any working examples yet, but we will soon.

While viewer.trackedEntity is useful if you want the camera control to rotate around an entity, or if an entity is moving and you want to follow it; a lot of users just want to zoom to a non-moving entity but keep the camera controls the same (similar to Google Earth). We’re adding a couple of new methods to make this easy

viewer.zoomTo(entity) - Zooms instantly to an entity, but does not change the camera controls or track it.

viewer.flyTo(entity) - The same as above, but flies to the entity instead.

viewer.zoomTo([entity, entity2, entity3]) - Zooms instantly to a group of entities, all entities will be in camera view (assuming they aren’t behind a mountain).

viewer.flyTo([entity, entity2, entity3]) - Same as above, but with an animated flight.

These properties will provide good defaults if you only specify the entities, but will also include options to control things such as length of flight, direction of view, etc… This should also make it easy for an app like Cesium Viewer to automatically fly to the extent of the data on load.

Since data sources use the Entity API, they also get all of the benefits I’ve describe above. So it’s easier to apply custom styling to GeoJSON data simply because the entity API is now easier to use.

We’re really excited about the new role the Entity API will be playing in Cesium. You can play with a hosted pre-release version of Sandcastle here. Most of the “old” Sandcastle primitive examples have been ported to the new API, this includes not just the geometries but models, billboards, etc… We’re also planning on more clean-up work to be done on our Sandcastle demos in the near term. Since this is a snapshot of our developer branch; there are known issues with some of the examples (like 3D model zoom issues). But don’t hesitate to let us know if you run into something you think may be a bug.

Most of these changes should be available starting with 1.6 on February 2nd and the Entity API will continue to evolve through the course of the year. (For example we plan on making highlighting an entity on pick or rollover really easy and customizable).

If you have any questions, feedback, or feature requests regarding the Entity API, please don’t hesitate to ask them here.

Thanks,

Matt

This is great! Can’t wait to try some of these new features out.

I’ve played with the entity API in the first couple releases of 1.0 and have wished for a lot of thing you mentioned above. I’ve been trying to figure out how to get a reference to the gltf model from my loaded czml and to orient the camera in a certain way around it. I thought Entity.Model was how I would do it but could never get it to work. I’m guessing things like this should be a lot easier in the coming releases. Great work guys!

Reads and looks amazingly simple.
Just 2 more weeks to wait :slight_smile:

Question: currently, you can use raise() and lower() to control the z-ordering of primitives (so that one displays on top of another), but not Entities. Will this feature be added to the new-and-improved Entity in 1.6?

bschow90: Eventually, yes; but not for the initial release. z-ordering in general is still not completely implemented under the hood in Cesium, mainly because it’s a much harder problem in 3D than in 2D. After the initial release of the API (and KML the month after), we’ll certainly rely on user feedback to figure out what issues require the most attention.

Now that Cesium 1.6 has been released, the previously hosted beta version of the Entity API has been taken down. We will continue to improve the Entity API throughout 2015 so be sure to try it out and continue to provide feedback.

Hi Matthew,

This will be very useful for us, thank you!!

Two remarks:

  • I found that using the viewerCesiumInspectorMixin (e.g. viewer.extend(Cesium.viewerCesiumInspectorMixin); ) seems to have a strange side effect that causes a polyline (or other geometry) to disappear when you zoom in.

  • On http://cesiumjs.org/2015/02/02/Visualizing-Spatial-Data/ the reference documentation for polyline points to PolylineGraphics while the example uses Polyline, which one is correct?

Regards, Willem

Thanks Willem.

To answer your first question, turning on the inspector turns on depth testing and terrain, both are off by default. Lines in Cesium don’t actually clamp to terrain yet (though they will very soon), so the line disappears because terrain is getting loaded over top of it. If the line were above the loaded terrain it wouldn’t disappear (if you are seeing something like that, please let me know).

For your second question, PolylineGraphics is what you want. All of the Entity API graphics objects have the Graphics suffix, which distinguishes them from the original Primitive API (which came first and has no suffix). I’ll make a note to mention this in the tutorial the next time I do an update pass.

Thanks again

Matt,

Love it. Kudos.

I was playing with the entity API tonight and ran into a question. Can time intervals apply to entities? If so, what is the proper syntax? I have worked over TimeInterval without success. Where, oh where have I gone wrong? Cheers, erik

have you tried the addInterval method

http://cesiumjs.org/Cesium/Build/Documentation/TimeIntervalCollection.html

I havn’t tried it but it may be …

availability : (new Cesium.TimeInterval()).addInterval(new TimeInterval({

start : Cesium.JulianDate.fromIso8601(‘2015-01-01T00:00:00Z’),

stop : Cesium.JulianDate.fromIso8601(‘2015-01-15T00:00:00Z’)

}) )

Eric,

You are missing the “new” in front of Cesium.TimeInterval, but that’s not the only problem. As Berwyn alluded to, availability is a TimeIntervalCollection, not a TimeInterval (maybe we should allow either for usability). Here’s a working example:

var viewer = new Cesium.Viewer(‘cesiumContainer’);

viewer.clock.startTime = Cesium.JulianDate.fromIso8601(‘2015-01-01T00:00:00Z’);

viewer.clock.stopTime = Cesium.JulianDate.fromIso8601(‘2015-01-15T00:00:00Z’);

viewer.clock.currentTime = viewer.clock.startTime.clone();

var entity = viewer.entities.add({

position: Cesium.Cartesian3.fromDegrees(-70.0,45.0),

ellipse : {

semiMinorAxis : 10000,

semiMajorAxis : 20000,

material : Cesium.Color.YELLOW.withAlpha(0.3),

outline : true,

outlineColor : Cesium.Color.YELLOW.withAlpha(0.5)

},

availability : new Cesium.TimeIntervalCollection()

});

entity.availability.addInterval(new Cesium.TimeInterval({

start : Cesium.JulianDate.fromIso8601(‘2015-01-01T00:00:00Z’),

stop : Cesium.JulianDate.fromIso8601(‘2015-01-15T00:00:00Z’)}));

viewer.zoomTo(viewer.entities);

I set the clock so the zoomTo succeeds. If you scrub around you’ll see the ellipse now disappears outside the time range. I’ll also right up an issue to allow TimeIntervalCollection to take an array of intervals in it’s constructor. In short, you’re original code should work in 1.7 (with the added new).

I’ve tried this and I found it very easy to use for most purposes. Great job there.
A few points:

  • +1 on the z-ordering issue.
  • I’m missing a way to hide/show an entity or a part of it. Is there such a way? Or do you intend to do it?
  • Another feature that might be very useful is inherent LOD support - to show/hide according to LOD. Can it be done with the current API?
  • In primitives (or primitiveCollection), there was the “update” function. Is there such a function now?
    Thanks :slight_smile:

Loving the Entity API!
Makes GeoJSONs really easy to style / transform in one place.

Thanks, Zach!

Yonatan:

  1. It’s a difficult problem in 3D, but we’ll get to it eventually.

  2. This will be in 1.7 (or 1.8 if I run into unexpected issues.)

  3. Not yet, but it’s definitely on our list of features. You can do this somewhat now for label/billboards/points using eyeOffset or translucencyByDistance.

  4. There is no explicit update when working with the Entity API. Updates happen as part of DataSourceDisplay.update, but the actual rendering is completely decoupled from the Entity instances.

Hi Matthew,

I am using a .gltf model that was added with the Entity API. The model is complex, so to improve efficiency I hide the model (entity.model.show = false) when zoomed out (and show it again when zooming in).

I noticed that a call to viewer.flyTo(entity, …) does not work if the model is not visible, this is according to the documentation (Promise is false if the entity is not currently visualized in the scene).

Do you know if there is a way to flyTo() hidden entities, or should I use the camera.flyTo() in this case?

Thanks, Willem

Why not just set show to try before calling flyTo? Or do you want it to stay hidden? We may change the behavior for 1.8 so that it will fly to entities not being shown as well, but that semantic makes things a little trickier.

Other than that, camera.flyTo would be the better way to go for now.

Hi Matthew,

Thanks for the reply.

Yes, the .gltf model will be hidden until the camera is close by, so for now I will try to use camera.flyTo().

Willem

Thank you very much for providing this, and the example code as well.

Fyi, the Entity documentation (https://cesiumjs.org/Cesium/Build/Documentation/Entity.html) still does not list availability among the options, although it is listed among the members.

Thanks Kirk, I just opened a pull request with the fix: https://github.com/AnalyticalGraphicsInc/cesium/pull/3069

Hi Matthew,

I posted this same question somewhere but figured this is a good place to post this question.

Do you have a list/document about best practices in coding against Cesium to achieve a very efficient code and executes in the most optimized way?

One example I could think of is, if I load 5000 entities into the Cesium’s viewer and each entity contain a PointGraphics and a ModelGraphics and depending on the Zoom level / Camera’s distance (height) I toggle the PointGraphics or ModelGraphics. At this point, I am trying to optimize the code by limiting the number of ModelGraphics. Is there a way in Cesium to reference just one instance of ModelGraphics across 5000 entities meaning that there is only one instance of ModelGraphics being shared across entities? Since the Position/Orientation is tied to an entity not to the ModelGraphics sharing would not be an issue. Basically I am thinking that 5000 instances of Entities would also mean 5000 copies of ModelGraphics regardless whether the model is exactly the same. In theory, if there’s a way to share the Model’s geometry or an instance of a Model across 5000 entities, it will improve performance and memory consumption. I tried this theory but I got some weird behavior (model does not get rendered) hence this post.

Thanks,

Lionel