<?xml version="1.0" encoding="utf-8" standalone="yes"?><feed xmlns="http://www.w3.org/2005/Atom">
  <title></title>
  <subtitle></subtitle>
  <id>https://www.endpointdev.com/blog/tags/cesium/</id>
  <link href="https://www.endpointdev.com/blog/tags/cesium/"/>
  <link href="https://www.endpointdev.com/blog/tags/cesium/" rel="self"/>
  <updated>2025-06-13T00:00:00+00:00</updated>
  <author>
    <name>End Point Dev</name>
  </author>
  
    <entry>
      <title>New Cesium KML-CZML Editor Features: Custom Data &amp; Styling, Google 3D Tiles, and More</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/06/new-czml-kml-editor-features/"/>
      <id>https://www.endpointdev.com/blog/2025/06/new-czml-kml-editor-features/</id>
      <published>2025-06-13T00:00:00+00:00</published>
      <author>
        <name>Dmitry Kiselev</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2025/06/new-czml-kml-editor-features/banner.webp&#34; alt=&#34;To the right, a satellite imagery map with blue and red gradient polygons drawn over it. To the left, controls for an app, including import/export, create entities, and a list of entities.&#34;&gt;&lt;/p&gt;
&lt;p&gt;I have made some updates to the &lt;a href=&#34;https://www.visionport.com/cesium-kml-czml-editor/&#34;&gt;Cesium KML-CZML editor&lt;/a&gt; I created and maintain.&lt;/p&gt;
&lt;p&gt;The most important additions and changes are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support for Google 3D tiles&lt;/li&gt;
&lt;li&gt;Support for writing many more features, including interpolation and time series data for some properties. There are still no editing capabilities for these properties, but while previously the editor would strip these values from the data, it will now copy them into the output file.&lt;/li&gt;
&lt;li&gt;Export to KML and KMZ&lt;/li&gt;
&lt;li&gt;Support for custom data and styling using that custom data&lt;/li&gt;
&lt;li&gt;Switched frontend framework from Vue to React&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adding support for Google 3D tiles is what caused me to create this major version update. In a nutshell, Cesium has its own way of adding reactivity to Entities and Vue doesn&amp;rsquo;t always play nice with it. If I add Google 3D tiles to the scene, it looks like that Cesium Entities have some references to the scene and that causes Vue to apply reactive getters and setters to the whole scene.&lt;/p&gt;
&lt;p&gt;So, I&amp;rsquo;ve switched to using React because it&amp;rsquo;s easier to control which parts should be reactive, as well as when and how you update Cesium Entities and UI components.&lt;/p&gt;
&lt;p&gt;The next important piece is the CZML exporter. The main issue here is that I don&amp;rsquo;t want to strip away features even if I don&amp;rsquo;t support editing them.&lt;/p&gt;
&lt;p&gt;Even if I don&amp;rsquo;t have the UI to edit certain properties or animations for properties, a user can load a CZML document with a mixture of supported and unsupported features without losing the unsupported features. That means that the exporter should be more robust and feature complete than editor itself.&lt;/p&gt;
&lt;p&gt;I will probably separate the exporter as standalone package because it is valuable on its own — I couldn&amp;rsquo;t find a way to export Cesium entities as CZML within the library.&lt;/p&gt;
&lt;h3 id=&#34;custom-data--styling&#34;&gt;Custom data &amp;amp; styling&lt;/h3&gt;
&lt;p&gt;This part is my favorite. I have some experience creating maps, and when you are working with maps you are not focused on polygons or billboards, you are focused on the data, while graphics are just a tool to represent it.&lt;/p&gt;
&lt;p&gt;The previous iteration of the KML-CZML editor was more oriented toward importing KML and massaging it to have the same look in Cesium as it would in Google Earth. That puts focus on graphical features. This time I wanted to make it easy to edit data and style things based on the data.&lt;/p&gt;
&lt;h4 id=&#34;demo&#34;&gt;Demo&lt;/h4&gt;
&lt;p&gt;We&amp;rsquo;ll load in some demo data from New York elections in &lt;code&gt;Mike4326.geojson&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/06/new-czml-kml-editor-features/map-with-geojson.webp&#34; alt=&#34;The CZML editor with all yellow polygons drawn over a region of New York&#34;&gt;&lt;/p&gt;
&lt;p&gt;From here, we&amp;rsquo;ll click on the &amp;ldquo;Data table &amp;amp; Conditional Styling&amp;rdquo; button. Then, in the menu that opens, we&amp;rsquo;ll set up Color by value so that we can display different regions differently by our selected attribute. Note the &amp;ldquo;Mike_prc&amp;rdquo; column which displays a percent value. The app will select a color from the gradient based on this value.The app will select a color from the gradient based on this value.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/06/new-czml-kml-editor-features/color-by-value-selecting-Mike_prc.webp&#34; alt=&#34;The editor with a full screen popup reading &amp;ldquo;conditional styling&amp;rdquo;. There are three main tabs, &amp;ldquo;Color by value&amp;rdquo;, &amp;ldquo;Labels&amp;rdquo;, and &amp;ldquo;Extrusion and scale&amp;rdquo;. The first is selected. Under the tabs it reads &amp;ldquo;Set entities colors by value of an attribute&amp;rdquo;. Under a dropdown reading &amp;ldquo;Attribute&amp;rdquo;, &amp;ldquo;Mike_prc&amp;rdquo; is selected. A blue to red gradient is below, with a continuous range of colors. At the bottom is a table with polygon 1, polygon 2, etc. Then a style column, all of which have a yellow box. The rightmost column is &amp;ldquo;Mike_prc&amp;rdquo;, and reads a percent number from 0 to 100.&#34;&gt;&lt;/p&gt;
&lt;p&gt;To group values together, we&amp;rsquo;ll toggle the &amp;ldquo;Fixed gradations&amp;rdquo; switch. Then hit the &amp;ldquo;Preview&amp;rdquo; button. By default, there are 9 gradations.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/06/new-czml-kml-editor-features/fixed-gradations-9-preview.webp&#34; alt=&#34;The same view, with the gradient now reduced to only 9 values. There is an additional column next to &amp;ldquo;style&amp;rdquo; which shows colors from the gradient corresponding to the Mike_prc value.&#34;&gt;&lt;/p&gt;
&lt;p&gt;We want fewer gradations than that. Let&amp;rsquo;s try out 6.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/06/new-czml-kml-editor-features/fixed-gradations-6-preview.webp&#34; alt=&#34;The same view, but now there are only 6 values in the gradient. Most of the preview colors are the same, but a few have been changed to match the 6 values.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Not much difference, but we&amp;rsquo;ll go with the 6 gradations. I&amp;rsquo;ll hit apply, and you can see the colors are now displayed in the &amp;ldquo;Style&amp;rdquo; column, while the &amp;ldquo;Preview&amp;rdquo; column has been hidden.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/06/new-czml-kml-editor-features/fixed-gradations-6-applied.webp&#34; alt=&#34;The same view, but now the red to blue colors appear in the &amp;ldquo;style&amp;rdquo; column, and the &amp;ldquo;preview column is hidden.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Let&amp;rsquo;s see how it looks on the map.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/06/new-czml-kml-editor-features/map-with-6-gradations.webp&#34; alt=&#34;The original view of the CZML editor. The electoral districts now show a gradient from blue to red, corresponding to the polygon styling we saw, rather than all being yellow as it started.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Very nice! Now let&amp;rsquo;s add some labels for a different attribute, the election district.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/06/new-czml-kml-editor-features/label-Election_D-selected.webp&#34; alt=&#34;The conditional styling menu, this time in the &amp;ldquo;Labels&amp;rdquo; tab. Text reads &amp;ldquo;Set Labels and Label text using entities attribute value. The selected attribute is &amp;ldquo;Election_D&amp;rdquo;. There is a box displaying pure black color above the table of polygons.&#34;&gt;&lt;/p&gt;
&lt;p&gt;You can see the text color defaults to black, which won&amp;rsquo;t read well on our colored districts. Let&amp;rsquo;s change it by hitting the &amp;ldquo;edit&amp;rdquo; button next to the color.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/06/new-czml-kml-editor-features/label-color-selected.webp&#34; alt=&#34;The same screen, now with added color sliders next to the color square. The color has been changed to a near-white gray.&#34;&gt;&lt;/p&gt;
&lt;p&gt;I changed it to a light gray. Let&amp;rsquo;s look at the map now.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/06/new-czml-kml-editor-features/map-with-labels.webp&#34; alt=&#34;A closer view of the map with the red-blue colored districts, now with many light gray labels displaying the name of the election district.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Lots of labels! This is quite a dense area, so you would probably want to zoom in further to see the labels more clearly.&lt;/p&gt;
&lt;p&gt;See my previous blog post &lt;a href=&#34;/blog/2020/12/cesium-kml-czml-editor/&#34;&gt;introducing the Cesium CZML-KML Editor&lt;/a&gt; for more about the Cesium CZML-KML editor.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Place Labels in Cesium using Vector Data Tiles</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2025/02/cesium-place-label-vector-data-tiles/"/>
      <id>https://www.endpointdev.com/blog/2025/02/cesium-place-label-vector-data-tiles/</id>
      <published>2025-02-13T00:00:00+00:00</published>
      <author>
        <name>Dmitry Kiselev</name>
      </author>
      <content type="html">
        &lt;style&gt;
ol &gt; * &gt; ol {
  list-style: lower-alpha;
}
&lt;/style&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/02/cesium-place-label-vector-data-tiles/banner.webp&#34; alt=&#34;A satellite map of a large area around New York City, with labels for cities scaled to their population.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Cesium is like an IKEA globe: it can do almost anything, but you have to put it together yourself. One major missing feature is labels for cities. Compare the default views of Google Earth and Cesium, and you&amp;rsquo;ll notice this immediately.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/02/cesium-place-label-vector-data-tiles/google-vs-cesium-default-view.webp&#34; alt=&#34;A side-by-side view of satellite imagery of the northeastern United States. On the left, there are state labels and state lines, with some large cities marked. On the right, there are no markings, just the satellite imagery.&#34;&gt;&lt;br&gt;
&lt;small&gt;Left: Google Earth default view, with labels and state lines. Right: Cesium default view, with no labels or markers.&lt;/small&gt;&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re reading this post, you might be able to find any major U.S. city without any clues, but most people can’t. You can overlay imagery that includes labels as part of the image, but this approach has a significant drawback: as soon as you rotate the view away from north-up, the labels become difficult to read.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/02/cesium-place-label-vector-data-tiles/imagery-with-labels-in-layer.webp&#34; alt=&#34;A side-by-side comparison of satellite imagery. There are labels for major roads and cities. On the left, these look normal as one would expect on a map, while on the right, the view has been tilted up to a higher angle, and the labels have been tilted as well, making them unreadable.&#34;&gt;&lt;/p&gt;
&lt;p&gt;In Google Earth, this issue is handled using vector data overlays. Labels, borders, and other similar elements are examples of how useful vector overlays can be.&lt;/p&gt;
&lt;p&gt;Cesium also supports vector features such as labels, polylines, and polygons. You can create them manually or use CZML or KML to add such features to a scene. If you use KML, Cesium translates it internally, but the result is the same. Vector features have distinct advantages for 3D geographic data, with labels being the most obvious example.&lt;/p&gt;
&lt;h3 id=&#34;challenges-of-vector-data-in-cesium&#34;&gt;Challenges of Vector Data in Cesium&lt;/h3&gt;
&lt;p&gt;Displaying vector data in Cesium comes with some challenges. Since we cannot load and display all data at once, we have two options: keep it as vector data or rasterize it into imagery. Either way, we need to tile the data, breaking it into small chunks and generalizing or simplifying it.&lt;/p&gt;
&lt;p&gt;Continuing with the example of labels: tiles covering large areas should include only essential labels (e.g., capital cities) to avoid excessive clutter. Similarly, for polylines such as roads or boundaries, we need to prioritize important roads and simplify their geometries.&lt;/p&gt;
&lt;p&gt;Most GeoServers and data providers serve vector tiles encoded as GeoJSON, KML, or other binary formats. However, integrating them into Cesium or Google Earth requires additional steps.&lt;/p&gt;
&lt;p&gt;There are two big considerations that need to be taken into account when integrating tiled vector data into any application:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;How particular geometric features (points, lines, and polygons) are encoded for every tile. Also, what are their attributes and how should they be represented and styled? This involves considering whether a point should have a label or an icon, what color should it be, etc.&lt;/li&gt;
&lt;li&gt;How the data is arranged. This includes how many levels it has, how you fetch new tiles, what the area each tile covers is, and how the software you are integrating with (in our case, Cesium) manages rendering, loading, and unloading the data.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In Google Earth, KML covers both issues in one technology. KML describes features and their styling, while for the arrangement of tiles you can use &lt;a href=&#34;https://www.google.com/earth/outreach/learn/using-network-links-effectively/&#34;&gt;Network Link Regions&lt;/a&gt;. To put it simply, Network Link Regions define an area and as soon as the camera gets close enough to that area, Google Earth loads a new chunk of data for that area. You can read more about that in the &lt;a href=&#34;https://developers.google.com/kml/documentation/regions#smart-loading-of-region-based-network-links&#34;&gt;KML docs&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In Cesium, this process is covered by multiple technologies, and not all of them work together. We have several encoding and styling options (CZML, KML, GeoJSON), but none of them natively support tiling.&lt;/p&gt;
&lt;p&gt;On the other hand, Cesium 3D Tiles work well for large 3D meshes and point clouds, but they do not support CZML, KML, or GeoJSON as tile content.&lt;/p&gt;
&lt;p&gt;To bridge this gap, Cesium provides &lt;a href=&#34;https://github.com/CesiumGS/3d-tiles/tree/vctr/TileFormats/VectorData&#34;&gt;the Vector Data tile format&lt;/a&gt; and &lt;a href=&#34;https://github.com/CesiumGS/3d-tiles/tree/main/specification/Styling&#34;&gt;3D Tiles Styling&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Although they are still experimental, these features work with the standard Cesium distribution without any hacks or undocumented options.&lt;/p&gt;
&lt;p&gt;Since I previously implemented city labels in Cesium, I decided to revisit the task to do it with this new standard in mind. In my earlier implementation, I had to rely on undocumented Cesium features to determine which tiles should be loaded and unloaded. More details about that approach can be found &lt;a href=&#34;/blog/2023/05/cesium-labels/&#34;&gt;in my previous blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h3 id=&#34;technical-implementation&#34;&gt;Technical Implementation&lt;/h3&gt;
&lt;p&gt;For this tileset generation, I use two standards:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vector Data tile content with tileset styling
&lt;ul&gt;
&lt;li&gt;Vector Data tile content defines how each tile is encoded&lt;/li&gt;
&lt;li&gt;Tileset styling determines how points, lines, and polygons are rendered&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Implicit tiling
&lt;ul&gt;
&lt;li&gt;Implicit tiling defines tile arrangement and availability&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both standards use a mix of JSON and binary-encoded features. In a nutshell, to encode a point with a label you have to write a vctr file, a subtree file and a tileset.json file.&lt;/p&gt;
&lt;p&gt;Each &lt;strong&gt;vctr&lt;/strong&gt; file consists of the following sections:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Header&lt;/strong&gt;: Defines technical data, like the size of the sections in bytes and their offsets&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Feature Table&lt;/strong&gt;: Defines how to read points, lines, or polygons from the binary data section In our case we just specify how many points we have&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Batch Table&lt;/strong&gt;: Defines data specific attributes, in our case that will be a name for a point&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Binary data with positions and indices&lt;/strong&gt;: Coordinates of the points optimized for storage size&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Subtree&lt;/strong&gt; files encode which child tiles are available, which of them have content, and what are the available subtrees.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;tileset.json&lt;/strong&gt; puts all that information together, defining what region the whole tileset covers, specifying that we are using implicit tiling, and defining URL templates for fetching &lt;strong&gt;vctr&lt;/strong&gt; and &lt;strong&gt;subtree&lt;/strong&gt; files.&lt;/p&gt;
&lt;p&gt;Add a style which tells Cesium that it should create a label for every point with its name attribute. And congratulations, we have our first set of labels!&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2025/02/cesium-place-label-vector-data-tiles/earth-with-labels.webp&#34; alt=&#34;Satellite imagery on a globe. There are labels on some of the biggest cities on the globe, such as Los Angeles, New York City, Bogota, etc.&#34;&gt;&lt;/p&gt;
&lt;h3 id=&#34;managing-uneven-label-distribution&#34;&gt;Managing Uneven Label Distribution&lt;/h3&gt;
&lt;p&gt;But as soon as we zoom in we hit a problem: we have too many labels too close to each other.&lt;/p&gt;
&lt;img src=&#34;/blog/2025/02/cesium-place-label-vector-data-tiles/uneven-label-distribution.webp&#34; alt=&#34;The northeastern United States, with labels on the largest cities. Most labels overlap with several others close by, while there are large areas with no labels at all.&#34; style=&#34;max-height:500px&#34;&gt;
&lt;p&gt;In order to avoid having the globe polluted with messy clumps of labels when zooming in, we have to discern how the visibility of 3D tile content is determined by Cesium. A key concept for handling this is assigning a &lt;code&gt;geometricError&lt;/code&gt; value to each tile. The exact value might be complex to calculate, but the general rule is: the larger the &lt;code&gt;geometricError&lt;/code&gt;, the bigger the distance from which a tile should be visible.&lt;/p&gt;
&lt;p&gt;So as you zoom in closer, labels from tiles with bigger &lt;code&gt;geometricError&lt;/code&gt; will appear sooner. Furthermore, for &lt;em&gt;implicit tiling&lt;/em&gt; the &lt;code&gt;geometricError&lt;/code&gt; of children tiles should be half of what their parent tiles’ &lt;code&gt;geometricError&lt;/code&gt; values are. Practically speaking, this means that the &lt;code&gt;geometricError&lt;/code&gt; value assigned to the root tile cascades throughout the system and changes how soon all of the labels become visible. To have some of the labels appear later than the others we have to push them down the tile tree.&lt;/p&gt;
&lt;p&gt;The first thing I tried was to set a minimum tree depth for a label based on city size, but the end result was pretty much the same:&lt;/p&gt;
&lt;img src=&#34;/blog/2025/02/cesium-place-label-vector-data-tiles/uneven-label-distribution-minimum-tree-depth.webp&#34; alt=&#34;The northeastern United States, looking essentially unchanged from the previous image.&#34; style=&#34;max-height:500px&#34;&gt;
&lt;p&gt;The root cause of the points clumping up is not small towns appearing too early, it’s how the populated places themself are distributed. “The Bronx” and “Manhattan” are just big and too close to each other and to “New York City”. If I try to push “Manhattan” deep enough so it doesn’t overlap with “New York City” based just on its size we won’t be able to see Boston or Baltimore or Hamilton, although they have plenty of space to be shown at this zoom level. Hence, we need a way to cluster points and determine point depth based on how much space there is between neighbours.&lt;/p&gt;
&lt;h3 id=&#34;clustering-labels&#34;&gt;Clustering Labels&lt;/h3&gt;
&lt;p&gt;Clustering is a well-established problem in computational geometry, and there are plenty of libraries to solve it, but if I used one of them I would also have to integrate it with the tree structure of the tiles. So instead I’ve implemented a straightforward approach that is effective, although not 100% correct mathematically:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For every level of the tree, calculate a distance threshold. The threshold gets smaller and smaller with every level of the tree. Let’s follow the same pattern as the tiles&amp;rsquo; &lt;code&gt;geometricError&lt;/code&gt; for this threshold&lt;/li&gt;
&lt;li&gt;For every point to be inserted into the tree, calculate the distance to a closest point in the tree&lt;/li&gt;
&lt;li&gt;
&lt;ol&gt;
&lt;li&gt;If the distance is more than the threshold, just insert the point. If the distance is smaller than the threshold, let’s calculate how many levels we should skip to get the threshold small enough to insert the point at this level&lt;/li&gt;
&lt;li&gt;If an inserted point is more important than an existing point, then put the inserted point at the current level and push the old one down the tree instead&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;And so, there you have it:&lt;/p&gt;
&lt;img src=&#34;/blog/2025/02/cesium-place-label-vector-data-tiles/working-labels.webp&#34; alt=&#34;The same northeastern United States area, with the labels mostly not overlapping, and with more mid-sized towns far from big cities labeled.&#34; style=&#34;max-height:500px&#34;&gt;
&lt;h3 id=&#34;next-steps&#34;&gt;Next Steps&lt;/h3&gt;
&lt;p&gt;Further steps I have in mind to expand on this work with vector tiles include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Enhancing styling options&lt;/li&gt;
&lt;li&gt;Encoding polylines&lt;/li&gt;
&lt;li&gt;Encoding polygons&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;notes&#34;&gt;Notes&lt;/h3&gt;
&lt;p&gt;The project is available on GitHub under the Apache-2.0 license: &lt;a href=&#34;https://github.com/EndPointCorp/cesium-vector-data-tiles&#34;&gt;https://github.com/EndPointCorp/cesium-vector-data-tiles&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Place data is from GeoNames.org under the CC-BY license.&lt;/p&gt;
&lt;p&gt;Screenshots use Bing Imagery, ArcGIS World Imagery, and Google Earth.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Compressed CZML</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2023/06/compressed-czml/"/>
      <id>https://www.endpointdev.com/blog/2023/06/compressed-czml/</id>
      <published>2023-06-13T00:00:00+00:00</published>
      <author>
        <name>Dmitry Kiselev</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2023/06/compressed-czml/crowded-city.webp&#34; alt=&#34;A crowded city on an overcast day. Tall apartment buildings fill the foreground while skyscrapers form a skyline in the background.&#34;&gt;&lt;/p&gt;
&lt;!-- Image by Zed Jensen, 2023. --&gt;
&lt;p&gt;Let’s talk about CZML, Cesium&amp;rsquo;s main language for specifying 3D scenes, and how to incorporate external resources such as billboard graphics, material textures, and 3D models into CZML files.&lt;/p&gt;
&lt;p&gt;For example, let’s look at how we can include glTF models.&lt;/p&gt;
&lt;p&gt;glTF models are composed of multiple files: a single JSON index file along with a variable number of binary buffer files and textures. So, in order to package CZML assets that include glTF models for distribution, you have to read the CZML document itself, then read the referenced glTF files. If they are not binary GLB files, you must also read the glTF files and package all of the files referenced by the glTF models. And if you find this paragraph cumbersome, that&amp;rsquo;s no accident. Indeed, the whole process is quite cumbersome!&lt;/p&gt;
&lt;p&gt;So we are dealing with something like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;CZML Document&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;glTF Model&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;buffer1.bin&lt;/li&gt;
&lt;li&gt;buffer2.bin&lt;/li&gt;
&lt;li&gt;texture1.png&lt;/li&gt;
&lt;li&gt;texture2.png&lt;/li&gt;
&lt;li&gt;…&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you want to keep the glTF model as a single asset, you can convert the glTF files into binary (GLB) files, and then embed them as base64 data links into CZML.&lt;/p&gt;
&lt;p&gt;Another example would be a set of points with billboards, let’s say a couple hundred points with plenty of different images. Keeping track of what images you have to ship along with a CZML document is very inconvenient, so you might want to embed the images.&lt;/p&gt;
&lt;p&gt;As with 3D models you could use base64 data links, but you will lose readability. You won’t be able to easily edit an image itself; you will have to decode it back into an image file, edit it, re-encode it, and write it back into the CZML file.&lt;/p&gt;
&lt;p&gt;And furthermore, while many billboards can share the same image, if you are going to encode it to base64, you either have to repeat the same base64-encoded string over and over or use &lt;a href=&#34;https://cesium.com/learn/cesiumjs/ref-doc/ReferenceProperty.html&#34;&gt;CZML reference properties&lt;/a&gt;. On the one hand, repeating the same base64 string makes a file terribly bloated, while on the other, with CZML reference properties you have to keep track of Entity IDs. Tracking Entity IDs means you need to be sure that you don’t accidentally delete the Entity which others refer to, and you’ll need to copy it if you want to split the dataset into different files.&lt;/p&gt;
&lt;p&gt;What I really want is to use the same approach that Google Earth uses for KMZ files: KMZ is just a ZIP archive with a KML document and referenced assets packed together. Let’s do the same trick with CZML.&lt;/p&gt;
&lt;p&gt;So the general approach would be:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read CZMZ ZIP archive.&lt;/li&gt;
&lt;li&gt;Index ZIP entries as blob objects.&lt;/li&gt;
&lt;li&gt;Find the main CZML document.&lt;/li&gt;
&lt;li&gt;Load it with Cesium CzmlDataSource and proxy all local URLs to blobs from step 2.&lt;/li&gt;
&lt;li&gt;Add destructor to DataSource to revoke blob URLs.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;To set a proxy we have two main options: read the whole CZML document as a JavaScript object and replace all of the URLs with &lt;a href=&#34;https://cesium.com/learn/cesiumjs/ref-doc/Resource.html&#34;&gt;Cesium.Resource&lt;/a&gt; objects using a proxy, or provide a Cesium.Resource to &lt;a href=&#34;https://cesium.com/learn/cesiumjs/ref-doc/CzmlDataSource.html#load&#34;&gt;CzmlDataSource.load&lt;/a&gt; instead of URLs. In most cases the second option is easier, unless you do some preprocessing on the CZML document before loading.&lt;/p&gt;
&lt;p&gt;You can read zip archives with a library of your choice. I’m using zip.js because Cesium already uses some methods from it.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; data = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; fetch(assetPath)).blob();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; reader = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; zip.ZipReader(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; zip.BlobReader(data));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; entriesMap = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Map();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; entry &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;of&lt;/span&gt; entries) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; blob = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;await&lt;/span&gt; entry.getData(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; zip.BlobWriter());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; blobURL = URL.createObjectURL(blob);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   entriesMap.set(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt; + entry.filename, blobURL);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now get the document:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; documentEntry = entries.find(e =&amp;gt; /&lt;span style=&#34;color:#a61717;background-color:#e3d2d2&#34;&gt;\&lt;/span&gt;.czml$/i.test(e.filename));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; documentBlob = entriesMap.get(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;/&amp;#39;&lt;/span&gt; + documentEntry.filename);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;And load the DataSource:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DataSourceInstance.load(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Cesium.Resource({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   url: documentBlob,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   proxy: {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      getURL: URL =&amp;gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^blob:/&lt;/span&gt;.test(URL)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; blobId = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; URL(URL.replace(&lt;span style=&#34;color:#080;background-color:#fff0ff&#34;&gt;/^blob:/&lt;/span&gt;, &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)).pathname;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; blobURL = entriesMap.get(blobId);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;               &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; blobURL ? blobURL : URL;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           console.warn(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;URL not found inside czmz&amp;#39;&lt;/span&gt;, URL);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;return&lt;/span&gt; URL;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;That’s mostly it. We just want to clean up after ourselves; we have to unregister blob URLs to free the resources.&lt;/p&gt;
&lt;p&gt;It’s not documented, but if you remove a DataSource from DataSourceCollection with the &lt;code&gt;destroy&lt;/code&gt; parameter set to true, and DataSource has a destroy method, it will be called.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;DataSourceInstance.destroy = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;let&lt;/span&gt; blobUrl &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;of&lt;/span&gt; entriesMap.values()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;       URL.revokeObjectURL(blobUrl);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


      </content>
    </entry>
  
    <entry>
      <title>Cesium Labels</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2023/05/cesium-labels/"/>
      <id>https://www.endpointdev.com/blog/2023/05/cesium-labels/</id>
      <published>2023-05-24T00:00:00+00:00</published>
      <author>
        <name>Dmitry Kiselev</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2023/05/cesium-labels/rome-map.webp&#34; alt=&#34;A 16th-century topographical map of ancient Rome. Buildings are drawn in simple, clear, engraved lines. Streets and important structures like the Pantheon are labeled in Latin.&#34;&gt;&lt;/p&gt;
&lt;!-- Image: Topographical Map of Ancient Rome by Nicolas Beatrizet, 1557. Public domain, acquired from https://www.nga.gov/collection/art-object-page.112707.html --&gt;
&lt;p&gt;Improving place labels in CesiumJS, the open source JavaScript library for 3D globes and maps, has been a longstanding request from some of our VisionPort clients. The display of labels embedded in imagery has not been up to their expectations. The names look upside down as the globe is rotated and there&amp;rsquo;s no option to change the language:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2023/05/cesium-labels/cesium-old-labels.webp&#34; alt=&#34;The old Cesium labels. The 3D camera is rotated so that the labels reading &amp;ldquo;New York&amp;rdquo;, &amp;ldquo;Kips Bay&amp;rdquo;, etc., are at a 90 degree angle, making it difficult to read.&#34;&gt;&lt;/p&gt;
&lt;p&gt;Cesium has vector labels which allow you to anchor some text to a point on a map which will always be aligned with the camera. However, there is no ready-to-use solution to display city names in particular and no way to load them according to a specific zoom level.&lt;/p&gt;
&lt;p&gt;To improve performance when displaying labels in Cesium it would make sense to load labels as a tile tree and only show some of the top of the tree at different zoom levels in a manner similar to how &lt;a href=&#34;https://developers.google.com/kml/documentation/regions?hl=en#smart-loading-of-region-based-network-links&#34;&gt;KML Regions with NetworkLinks&lt;/a&gt; work. Unfortunately, although Cesium supports KML, it doesn’t support KML Regions and NetworkLinks updates on Region change.&lt;/p&gt;
&lt;p&gt;Another off-the-shelf solution that might conceivably work would be to use Cesium 3D tiles, but unfortunately, tiles do not support 2D Billboards.&lt;/p&gt;
&lt;p&gt;Calculating regions and their level of detail is complicated, but Cesium already does most of that work for us. Cesium already calculates visible tiles and their corresponding levels of detail for ImageryProviders. An ImageryProvider is supposed to load imagery for a given tile’s coordinates and zoom level, which is almost what we want, except that we want to render some 3D primitives for labels, not 2D images for the earth surface.&lt;/p&gt;
&lt;p&gt;We can read what tiles are going to be rendered by Cesium and store them in a variable:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-javascript&#34; data-lang=&#34;javascript&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;const&lt;/span&gt; tilesToRender = viewer.scene.globe._surface._tilesToRender;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then, we get the &lt;a href=&#34;https://wiki.openstreetmap.org/wiki/TMS&#34;&gt;TMS&lt;/a&gt; coordinates out of the visible tiles on screen. Then we calculate the difference between the currently visible tiles and the new ones. We grab all the new tiles that we need from that list, and then we create the &lt;a href=&#34;https://cesium.com/learn/cesiumjs/ref-doc/Entity.html&#34;&gt;Entities&lt;/a&gt; with labels and some other styling properties.&lt;/p&gt;
&lt;h3 id=&#34;backend-and-data-source&#34;&gt;Backend and data source&lt;/h3&gt;
&lt;p&gt;To source the labels we use &lt;a href=&#34;https://www.geonames.org/&#34;&gt;GeoNames&lt;/a&gt;. They have a nice dataset for cities with their population included in the data. We take the city labels they provide and store them into a quadtree. Each node in the quadtree has 10 labels associated with it, starting with the largest population at the top of the data structure and going down. Based on the altitude we traverse a certain distance down the quadtree returning all cities from each node that corresponds to the tile requested.&lt;/p&gt;
&lt;p&gt;Here is a video demonstrating how labels display with this new feature we’ve developed:&lt;/p&gt;
&lt;p&gt;&lt;video type=&#34;video/mp4&#34; controls src=&#34;/blog/2023/05/cesium-labels/cesium-new-labels.mp4&#34; style=&#34;max-height:30rem;width:auto&#34;&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h3 id=&#34;future-plans&#34;&gt;Future Plans&lt;/h3&gt;
&lt;p&gt;Currently, the entity creation is done on the frontend, but we are planning to move this to the backend sometime soon. There is also some crowding of the labels when zooming out since surface tiles aren&amp;rsquo;t immediately updated. The tiles can likely be filtered to remove unneeded ones more aggressively.&lt;/p&gt;
&lt;h3 id=&#34;running-on-your-system&#34;&gt;Running On Your System&lt;/h3&gt;
&lt;p&gt;We have released this code &lt;a href=&#34;https://github.com/EndPointCorp/tiled-city-labels&#34;&gt;on our GitHub&lt;/a&gt; under the Apache license for everyone to use.&lt;/p&gt;
&lt;p&gt;To get this up and running on your local system, start with a Git clone:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;git clone https://github.com/EndPointCorp/tiled-city-labels.git&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then within the repo there’s a README to follow. We will give instructions here, but if they differ from the README follow those instead:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; tiled-city-labels
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker build -t cesium-labels .
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;docker run -p 48088:48088 --rm cesium-labels&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Then in a new terminal window still inside the project:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#038&#34;&gt;cd&lt;/span&gt; demo/
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;python -m SimpleHTTPServer &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8000&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now you can navigate to http://localhost:8000/ in your browser and see the city labels up and running. To customize this to your own system, you can use the mixin &lt;code&gt;demo/js/CitiesDataSource.js&lt;/code&gt; and add that to your own project. You can also change the port/server used in the mixin by editing the &lt;code&gt;fetch&lt;/code&gt; command inside the &lt;code&gt;queryData&lt;/code&gt; function.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>CZML-KML Editor Geometry Editing</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2021/04/czml-kml-editor-geometry-editing/"/>
      <id>https://www.endpointdev.com/blog/2021/04/czml-kml-editor-geometry-editing/</id>
      <published>2021-04-13T00:00:00+00:00</published>
      <author>
        <name>Dmitry Kiselev</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2021/04/czml-kml-editor-geometry-editing/czml-kml-geometry-editing.jpg&#34; alt=&#34;Screenshot of CZML-KML editor showing geometry editing feature&#34;&gt;&lt;/p&gt;
&lt;p&gt;We are happy to introduce a new feature for the Cesium CZML-KML Editor: polygons and polylines geometry editing. You can now edit geometries for existing entities and move entered points during the creation process. Here is a video with a short summary of the editing process:&lt;/p&gt;
&lt;iframe style=&#34;margin-bottom: 1em&#34; width=&#34;560&#34; height=&#34;315&#34; src=&#34;https://www.youtube-nocookie.com/embed/rLhy35_X5iA&#34; title=&#34;YouTube video player&#34; frameborder=&#34;0&#34; allow=&#34;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&#34; allowfullscreen&gt;&lt;/iframe&gt;
&lt;p&gt;See our previous blog post &lt;a href=&#34;/blog/2020/12/cesium-kml-czml-editor/&#34;&gt;introducing the Cesium CZML-KML Editor&lt;/a&gt; for further reference.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Announcing the Cesium KML-CZML Editor</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2020/12/cesium-kml-czml-editor/"/>
      <id>https://www.endpointdev.com/blog/2020/12/cesium-kml-czml-editor/</id>
      <published>2020-12-21T00:00:00+00:00</published>
      <author>
        <name>Dmitry Kiselev</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-00.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;End Point’s immersive technology team is happy to present a great new tool for the rapidly growing Cesium community: &lt;a href=&#34;https://www.visionport.com/cesium-kml-czml-editor/&#34;&gt;Cesium KML-CZML Editor&lt;/a&gt;. The editor gives users the ability to visually and dynamically edit KML and CZML in its Cesium browser window. Updates made with it can be exported at any time to CZML, the native markup language for Cesium.&lt;/p&gt;
&lt;p&gt;The Cesium KML-CZML Editor addresses an important but hitherto unaddressed need of the Cesium community: It provides an intuitive interface for making adjustments to fix the many inconsistencies with how KML created for (and often by) Google Earth appears on 3D maps rendered with Cesium. It is a powerful tool for converting and adapting KML for Google Earth into CZML that displays nicely in Cesium. The editor also works as a visual editor for creating and editing CZML, regardless of whether you’re converting from KML.&lt;/p&gt;
&lt;p&gt;The inconsistencies with how Cesium displays KML created for Google Earth are due to occasional differences between how Cesium and Google Earth render KML when various attributes aren’t specifically set within a given instance of code. The situation is similar to how web browsers sometimes interpret given instances of HTML differently. Just as with HTML, KML doesn’t require every attribute to be defined in a given instance of markup code.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-01.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;On the left side we have the editor toolbar, and on the right side the Cesium globe showing loaded or created entities the way they would appear in Cesium.&lt;/p&gt;
&lt;p&gt;The Editor Toolbar consist of the following areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;File Import: you can load KML, KMZ, CZML and GeoJSON formatted data, and you can load multiple files to combine them into one CZML document.&lt;/li&gt;
&lt;li&gt;Creation tools.&lt;/li&gt;
&lt;li&gt;A list of uploaded or created entities (will appear after upload or creation of a new entity).&lt;/li&gt;
&lt;li&gt;The actual editor for entity properties (will appear after entity selection).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Let’s start with a very basic example of highlighting a building by marking it with a pin in Google Earth:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-02.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now let’s move the camera about in different ways, especially noticing how the pin appears when zooming in and out. The pin stays close to the top of the building.&lt;/p&gt;
&lt;video controls&gt;
  &lt;source src=&#34;/blog/2020/12/cesium-kml-czml-editor/video-0.webm&#34; type=&#34;video/webm&#34;&gt;
  Sorry, your browser doesn’t support embedded videos.
&lt;/video&gt;
&lt;p&gt;Then let’s open the same KML in Cesium with a 3D building tileset loaded, to compare apples to apples:&lt;/p&gt;
&lt;video controls&gt;
  &lt;source src=&#34;/blog/2020/12/cesium-kml-czml-editor/video-1.webm&#34; type=&#34;video/webm&#34;&gt;
  Sorry, your browser doesn’t support embedded videos.
&lt;/video&gt;
&lt;p&gt;It’s easy to see that the green frame which is placed around the geographic position stays around the building, but the icon point of rotation in Cesium doesn’t match the one in Google Earth and the pin falls through the roof and lifts up unfortunately high above the building.&lt;/p&gt;
&lt;p&gt;KML doesn’t enforce certain aspects of its representation. If certain attributes are not specified or can’t be implemented exactly, then the KML browsers have the freedom to set different default values for them.&lt;/p&gt;
&lt;p&gt;In this example, Cesium and Google Earth display different pixel offsets for the pin icon, because in KML the offset is specified as a ratio of the image, but Cesium specifies the offset in pixels. Cesium converts the pixel offset of the pin from the ratio specified, but with some errors. Cesium reverses the X axis, and in some cases Cesium also calculates the pin icon image size incorrectly. So here it calculates the X offset as -4, which moves the pin icon to the left. To fix that, we can reverse the X axis back to 4 or even move the pin icon 8 pixels further to the right.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-03.jpg&#34; alt=&#34;&#34;&gt;
🡆
&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-04.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;The next issue is how the Z coordinate (altitude) is interpreted. Is it above sea level, above land according to some averaged model of the land, or on the land directly below a point?&lt;/p&gt;
&lt;p&gt;Note that 3D tilesets in Cesium are absolutely positioned. This means that if you show 3D buildings and want your pin to stick to a building, it’s better to use absolute elevation. Hence, it makes sense to set Height Reference to &lt;code&gt;NONE&lt;/code&gt;, which will force Cesium to use the absolute pin position.&lt;/p&gt;
&lt;p&gt;Another option in cases like this is to use relative elevation. This can be helpful if you don’t use 3D tiles, but instead use 3D terrain data, for example, with Cesium World Terrain or Maptiler. It’s especially helpful if you will be switching 3D terrain on and off. In this case, a relatively positioned pin will follow the elevation regardless of whether 3D terrain is turned on or off.&lt;/p&gt;
&lt;p&gt;To set it up, adjust Altitude and set Height Reference to &lt;code&gt;RELATIVE_TO_GROUND&lt;/code&gt;:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-05.jpg&#34; alt=&#34;&#34;&gt;
&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-06.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Here are the results for both options:&lt;/p&gt;
&lt;video controls&gt;
  &lt;source src=&#34;/blog/2020/12/cesium-kml-czml-editor/video-2.webm&#34; type=&#34;video/webm&#34;&gt;
  Sorry, your browser doesn’t support embedded videos.
&lt;/video&gt;
&lt;p&gt;As you can see, now we have the pin-point of the marker stuck to the desired location.&lt;/p&gt;
&lt;p&gt;These are the basics for how to match pins created in Google Earth with their Cesium representations, but in Cesium there are many more options for rendering pins. You can read the &lt;a href=&#34;https://cesium.com/docs/cesiumjs-ref-doc/BillboardGraphics.html&#34;&gt;relevant Cesium documentation&lt;/a&gt; (in Cesium terminology Pins are called Billboards) or interactively explore a live version of this editor to change color, rotation, translucency by distance, and other properties.&lt;/p&gt;
&lt;p&gt;Now let’s look at a polygon created in KML for Google Earth:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-07.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Then let’s open it in Cesium:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-08.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Well, we can select a polygon, but we don’t see it. This happens because the 3D terrain data covers the polygon. Just changing the Height reference helps, but it’s still not what we want:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-09.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Cesium has some default assumptions about polygons and rectangles and when to clamp them to the ground.&lt;/p&gt;
&lt;p&gt;As commented in the &lt;a href=&#34;https://sandcastle.cesium.com/?src=Clamp%20to%20Terrain.html&#34;&gt;Cesium SandCastle Clamp to Terrain demo code&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Corridors, polygons and rectangles will be clamped automatically if they are filled with a constant color and have no height or extruded height.&lt;/p&gt;
&lt;p&gt;NOTE: Setting height to 0 will disable clamping.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;So to get a polygon actually clamped to ground, we have to delete the Height property, not just set it to 0:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-10.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now let’s add a 3D tileset:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-11.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;And again, this is probably not exactly what you wanted. By default, Cesium doesn’t know what type of 3D tileset you would prefer, or whether you want the polygons to cover the tiles, or the tiles to cover the polygons. If you have a fairly large polygon covering a whole city, and a fairly low resolution 3D tileset, you probably want to highlight the whole area. But in the example above, we want our polygon to stick to the ground but not the buildings.&lt;/p&gt;
&lt;p&gt;Next, let’s use the Classification Type field, setting it to “TERRAIN” to select what the clamped polygon applies to:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-12.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Now, let’s open some exported data. Here’s a typical set of exported points generated with geojson.io:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-13.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Let’s choose a point and change its icon and its icon’s vertical alignment (so it doesn’t fall through the terrain) and dimensions:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-14.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Here we have selected one icon and updated its properties to make it appear as we want, but changing all the icons one by one would take forever, especially for large data sets.&lt;/p&gt;
&lt;p&gt;Click “COPY TO OTHER PINS”:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-15.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;On the left is a list with all the entities to which the changed properties are applicable and on the right is the list of the properties that were changed for the model entity selected. By default, all the properties are checked to be copied to applicable entities but in this example we only want to change the vertical alignment for all the pins, but want to keep the icon and icon dimension of the other pins unchanged. So we uncheck the image, width and height properties:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-16.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;Below we see how our scene looks after the update is applied. All the pins are above the ground, but the icons and dimensions are not changed:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;/blog/2020/12/cesium-kml-czml-editor/image-17.jpg&#34; alt=&#34;&#34;&gt;&lt;/p&gt;
&lt;p&gt;After you have all the data styled the way you want, download your CZML!&lt;/p&gt;
&lt;p&gt;The Cesium KML-CZML Editor code is Apache 2 licensed. It is &lt;a href=&#34;https://github.com/EndPointCorp/cesium-kml-czml-editor&#34;&gt;available here&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Liquid Galaxy at 2017 BOMA International Annual Conference &amp; Expo</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/07/liquid-galaxy-at-2017-boma/"/>
      <id>https://www.endpointdev.com/blog/2017/07/liquid-galaxy-at-2017-boma/</id>
      <published>2017-07-03T00:00:00+00:00</published>
      <author>
        <name>Ben Witten</name>
      </author>
      <content type="html">
        &lt;p&gt;&lt;a href=&#34;/blog/2017/07/liquid-galaxy-at-2017-boma/image-0-big.png&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; data-original-height=&#34;792&#34; data-original-width=&#34;1364&#34; height=&#34;372&#34; src=&#34;/blog/2017/07/liquid-galaxy-at-2017-boma/image-0.png&#34; width=&#34;640&#34;/&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;We just returned from Nashville after bringing &lt;a href=&#34;https://www.visionport.com/&#34;&gt;Liquid Galaxy&lt;/a&gt; to the 2017 BOMA International Annual Conference &amp;amp; Expo. Commercial real estate has always been a seamless fit for Liquid Galaxy due to the system’s ability to showcase real estate and property data in an interactive and panoramic setting.  Within real estate, Liquid Galaxy was first used by CBRE and has since been adopted by Hilton, JLL, and Prologis to name a few.&lt;/p&gt;
&lt;p&gt;In preparation for BOMA (Building Owners and Managers Association), we prepared sample commercial real estate content on our content management system to be displayed on the Liquid Galaxy. This included the creation of content about Hudson Yards, the new development being built in lower Manhattan.&lt;/p&gt;
&lt;p&gt;The content that was created demonstrates how brokers and directors at commercial real estate companies can tell an immersive, panoramic, and interactive story to their potential clients and investors. Future developments can be shown in developing areas, and with the content management system you can create stories that include videos, images, and 3D models of these future developments. The following video demonstrates this well:&lt;/p&gt;
&lt;iframe allowfullscreen=&#34;&#34; frameborder=&#34;0&#34; height=&#34;360&#34; src=&#34;https://www.youtube.com/embed/OGlDv1NHD7A&#34; width=&#34;640&#34;&gt;&lt;/iframe&gt;
&lt;p&gt;We were able to effectively showcase our ability to incorporate 3D models and mapping layers at BOMA through the use of Google Earth, Cesium, ArcGIS, Unity, and Sketchfab. We were also able to pull data and develop content for neighboring booths and visitors, demonstrating what an easy and data-agnostic platform Liquid Galaxy can be.&lt;/p&gt;
&lt;p&gt;We’re very excited about the increasing traction we have in the real estate industry, and hope our involvement with BOMA will take that to the next level. If you’d like to learn more about Liquid Galaxy, please visit &lt;a href=&#34;https://www.visionport.com/&#34;&gt;our website&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Successful First GEOINT Symposium for End Point Liquid Galaxy</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/06/successful-first-geoint-symposium-for/"/>
      <id>https://www.endpointdev.com/blog/2017/06/successful-first-geoint-symposium-for/</id>
      <published>2017-06-16T00:00:00+00:00</published>
      <author>
        <name>Ben Witten</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2017/06/successful-first-geoint-symposium-for/image-0-big.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; data-original-height=&#34;1201&#34; data-original-width=&#34;1600&#34; height=&#34;240&#34; src=&#34;/blog/2017/06/successful-first-geoint-symposium-for/image-0.jpeg&#34; width=&#34;320&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;This past week, End Point attended GEOINT Symposium to showcase &lt;a href=&#34;https://www.visionport.com/&#34;&gt;Liquid Galaxy&lt;/a&gt; as an immersive panoramic GIS solution to GEOINT attendees and exhibitors alike.&lt;/p&gt;
&lt;p&gt;At the show, we showcased Cesium integrating with ArcGIS and WMS, Google Earth, Street View, Sketchfab, Unity, and panoramic video. Using our Content Management System, we created content around these various features so visitors to our booth could take in the full spectrum of capabilities that the Liquid Galaxy provides.&lt;/p&gt;
&lt;p&gt;Additionally, we were able to take data feeds for multiple other booths and display their content during the show! Our work served to show everyone at the conference that the Liquid Galaxy is a data-agnostic immersive platform that can handle any sort of data stream and offer data in a brilliant display. This can be used to show your large complex data sets in briefing rooms, conference rooms, or command centers.&lt;/p&gt;
&lt;p&gt;Given the incredible draw of the Liquid Galaxy, the GEOINT team took special interest in our system and formally interviewed Ben Goldstein in front of the system to learn more! You can view the video of the interview here:&lt;/p&gt;
&lt;iframe allowfullscreen=&#34;&#34; frameborder=&#34;0&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/w6aEGE8lmrg&#34; width=&#34;560&#34;&gt;&lt;/iframe&gt;
&lt;p&gt;We look forward to developing the relationships we created at GEOINT, and hope to participate further in this great community moving forward. If you would like to learn more please visit &lt;a href=&#34;https://www.visionport.com/&#34;&gt;our Liquid Galaxy website&lt;/a&gt;!&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;/blog/2017/06/successful-first-geoint-symposium-for/image-1-big.png&#34; imageanchor=&#34;1&#34;&gt;&lt;img border=&#34;0&#34; data-original-height=&#34;845&#34; data-original-width=&#34;1504&#34; height=&#34;225&#34; src=&#34;/blog/2017/06/successful-first-geoint-symposium-for/image-1.png&#34; width=&#34;400&#34;/&gt;&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Full Cesium Mapping on the Liquid Galaxy</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/02/full-cesium-mapping-on-liquid-galaxy/"/>
      <id>https://www.endpointdev.com/blog/2017/02/full-cesium-mapping-on-liquid-galaxy/</id>
      <published>2017-02-07T00:00:00+00:00</published>
      <author>
        <name>Dave Jenkins</name>
      </author>
      <content type="html">
        &lt;p&gt;A few months ago, we shared a video and some &lt;a href=&#34;/blog/2016/07/cesium-on-liquid-galaxy/&#34;&gt;early work&lt;/a&gt; we had done with bringing the Cesium open source mapping application to the Liquid Galaxy. We&amp;rsquo;ve now completed a full deployment for &lt;a href=&#34;https://www.smartrac-group.com/&#34;&gt;Smartrac&lt;/a&gt;, a retail tracking analytics provider, using Cesium in a production environment!  This project presented a number of technical challenges beyond the early prototype work, but also brought great results for the client and garnered a fair amount of attention in the press, to everyone&amp;rsquo;s benefit.&lt;/p&gt;
&lt;iframe allowfullscreen=&#34;&#34; frameborder=&#34;0&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/CntuRx3Nig4&#34; width=&#34;560&#34;&gt;&lt;/iframe&gt;
&lt;p&gt;Cesium is an open source mapping application that separates out the tile sets, elevation, and markup language. This separation allows for flexibility at each major element:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We can use a specific terrain elevation data set while substituting any one of several map “skins” to drape on that elevation: a simple color coded map, a nighttime illumination map, even a water-colored “pirate map” look.&lt;/li&gt;
&lt;li&gt;For the terrain, we can download as much or as little is needed: As the Cesium viewer zooms in on a given spot, Cesium uses a sort of fractal method to download finer and finer resolution terrains in just the surrounding area, eventually getting to the data limit of the set. This gradual approach balances download requirements with viewable accuracy. In our case, we downloaded an entire terrain set up to level 14 (Earth from high in space is level 1, then zooms in to levels 2, 3, 4, etc.) which gave us a pretty good resolution while conserving disk space. (The data up to level 14 totaled about 250 GB.)&lt;/li&gt;
&lt;li&gt;Using some KML tools we have developed for past projects and adapting to CZML (“cesium language”, get it?), we were able to take Smartrac&amp;rsquo;s supply chain data and show a comprehensive overview of the product flow from factories in southeast Asia through a distribution center in Seattle and on to retail stores throughout the Western United States.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The debut for this project was the National Retail Federation convention at the Javitz Center in New York City. Smartrac (and we also) wanted to avoid any show-stoppers that might come from a sketchy internet connection. So, we downloaded the map tiles, a terrain set, built our visualizations, and saved the whole thing locally on the head node of the Liquid Galaxy server stack, which sat in the back of the booth behind the screens.&lt;/p&gt;
&lt;p&gt;The show was a great success, with visitors running through the visualizations almost non-stop for 3 days. The client is now taking the Liquid Galaxy and the Cesium visualizations on to another convention in Europe next month. The NRF, IBM, and several other ecommerce bloggers wrote up the platform, which brings good press for Smartrac, Cesium, and the Liquid Galaxy.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Smartrac’s Liquid Galaxy at National Retail Federation</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2017/01/smartracs-liquid-galaxy-at-national/"/>
      <id>https://www.endpointdev.com/blog/2017/01/smartracs-liquid-galaxy-at-national/</id>
      <published>2017-01-25T00:00:00+00:00</published>
      <author>
        <name>Ben Witten</name>
      </author>
      <content type="html">
        &lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: center;&#34;&gt;&lt;a href=&#34;/blog/2017/01/smartracs-liquid-galaxy-at-national/image-0-big.jpeg&#34; imageanchor=&#34;1&#34; style=&#34;clear: right; float: right; margin-bottom: 1em; margin-left: 1em;&#34;&gt;&lt;img border=&#34;0&#34; height=&#34;300&#34; src=&#34;/blog/2017/01/smartracs-liquid-galaxy-at-national/image-0.jpeg&#34; width=&#34;400&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Last week, Smartrac exhibited at the retail industry’s BIG Show, &lt;a href=&#34;https://nrfbigshow.nrf.com/&#34;&gt;NRF 2017&lt;/a&gt;, using a &lt;a href=&#34;https://www.visionport.com/&#34;&gt;Liquid Galaxy&lt;/a&gt; with custom animations to showcase their technology.&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://www.smartrac-group.com/&#34;&gt;Smartrac&lt;/a&gt; provides data analytics to retail chains by tracking physical goods with NFC and Bluetooth tags that combine to track goods all the way from the factory to the distribution center to  retail stores.  It’s a complex but complete solution.  How best to visualize all that data and show the incredible value that Smartrac brings?  Seven screens with real time maps in Cesium, 3D store models in Unity, and browser-based dashboards, of course. End Point has been working with Smartrac for a number of years as a development resource on their SmartCosmos platform, helping them with UX and back-end database interfaces. This work included development of REST-based APIs for data handling, as well as a Virtual Reality project utilizing the Unity game engine to visualize data and marketing materials directly on several platforms including the Oculus Rift, the Samsung Gear 7 VR, and WebGL.  Bringing that back-end work forward in a highly visible platform for the retail conference was a natural extension for them, and the Liquid Galaxy fulfilled that role perfectly. The large Liquid Galaxy display allowed Smartrac to showcase some of their tools on a much larger scale.&lt;/p&gt;
&lt;p&gt;For this project, End Point deployed two new technologies for the Liquid Galaxy:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Cesium Maps&lt;/strong&gt; - Smartrac had two major requirements for their data visualizations: show the complexity of the solution and global reach, while also making the map data offline wherever possible to avoid the risk of sketchy Internet connections at the convention center (a constant risk).  For this, we deployed &lt;a href=&#34;http://www.cesiumjs.org&#34;&gt;Cesium&lt;/a&gt; instead of Google Earth, as it allowed for a fully offline tileset that we could store locally on the server, as well as providing a rich data visualization set (&lt;a href=&#34;https://www.youtube.com/watch?v=e0xbeQGUoa8&#34;&gt;we’ve shown other examples before&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unity3D Models&lt;/strong&gt; - Smartrac also wanted to show how their product tracking works in a typical retail store.  Rather than trying to wire a whole solution during the short period for a convention, however, they made the call to visualize everything with &lt;a href=&#34;https://unity3d.com/&#34;&gt;Unity&lt;/a&gt;, a very popular 3D rendering engine.  Given the multiple screens of the Liquid Galaxy, and our ability to adjust the view angle for each screen in the arch around the viewers, this Unity solution would be very immersive and able to tell their story quite well.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Smartrac showcased multiple scenes that incorporated 3D content with live data, labels superimposed on maps, and a multitude of supporting metrics. End Point developers worked on custom animation to show their tools in a engaging demo. During the convention, Smartrac had representatives leading attendees through the Liquid Galaxy presentations to show their data. Video of these presentations can be viewed below.&lt;/p&gt;
&lt;iframe allowfullscreen=&#34;&#34; frameborder=&#34;0&#34; height=&#34;394&#34; src=&#34;https://www.youtube.com/embed/CntuRx3Nig4&#34; width=&#34;700&#34;&gt;&lt;/iframe&gt;
&lt;p&gt;Smartrac’s Liquid Galaxy received positive feedback from everyone who saw it, exhibitors and attendees alike. Smartrac felt it was a great way to walk through their content, and attendees both enjoyed the content and were intrigued by the display on which they were seeing the content. Many attendees who had never seen Liquid Galaxy inquired about it.&lt;/p&gt;
&lt;p&gt;If you’d like to learn more about Liquid Galaxy or new projects we are working on or having custom content developed, please visit our &lt;a href=&#34;https://www.visionport.com/&#34;&gt;Liquid Galaxy website&lt;/a&gt; or &lt;a href=&#34;https://www.visionport.com/contact/&#34;&gt;contact us here&lt;/a&gt;.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Cesium on the Liquid Galaxy</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/07/cesium-on-liquid-galaxy/"/>
      <id>https://www.endpointdev.com/blog/2016/07/cesium-on-liquid-galaxy/</id>
      <published>2016-07-13T00:00:00+00:00</published>
      <author>
        <name>Dave Jenkins</name>
      </author>
      <content type="html">
        &lt;p&gt;Data visualization continues to evolve, with ever-more complex data sets available openly, and a corresponding increased pace in visualization tools. In mapping GIS data, the Cesium app is gaining quite a bit of traction. As we continue to branch out with new functionality and visualization apps for the &lt;a href=&#34;https://www.visionport.com/&#34;&gt;Liquid Galaxy&lt;/a&gt;, we wanted to try the Cesium app as well.&lt;/p&gt;
&lt;iframe allowfullscreen=&#34;&#34; frameborder=&#34;0&#34; height=&#34;315&#34; src=&#34;https://www.youtube.com/embed/e0xbeQGUoa8&#34; width=&#34;560&#34;&gt;&lt;/iframe&gt;
&lt;p&gt;Cesium is written all in JavaScript WebGL and offers some nice advantages over other engines: it’s open source, it’s flexible, and it’s quick. It can accept an array of points, shapefiles, 3D models, and even KML.  The JavaScript then chews these up and delivers a nice consistent 3D environment that we can fly through with the SpaceNav controller, set scenes in a presentation to tell a story, or mix together with video or graphic popups for a fully immersive multimedia experience. Cesium is open source, and provides a great deal of flexibility and accessibility to build different kinds of data visualizations and interactions. There are a lot of new startups exploiting this platform and we welcome the opportunity to work with them.&lt;/p&gt;
&lt;p&gt;As we’ve written previously, the main advantage of the Liquid Galaxy platform is the ability to adjust the viewing angle on each screen to match the physical offset, avoiding (as much as possible) artificial distortions, fisheye views, or image stretching.  The trickiest bit of this project was setting the distributed camera driver, which takes input from the SpaceNav controller and correctly aligns the view position for each of the geometrically offset displays. Once the math is worked out, it’s relatively quick work to put the settings into a rosbridge WebSockets driver.  Once again, we’re really enjoying the flexibility that the ROS architecture grants this system.&lt;/p&gt;
&lt;p&gt;Looking forward, we anticipate this can open up many more visualizations for the Liquid Galaxy. As we continue to roll out in corporate, educational, and archival environments such as real estate brokerages, hospitality service providers, universities, and museums, the Cesium platform will offer yet another way for our customers to visualize and interact with their data.&lt;/p&gt;

      </content>
    </entry>
  
    <entry>
      <title>Story telling with Cesium</title>
      <link rel="alternate" href="https://www.endpointdev.com/blog/2016/03/story-telling-with-cesium/"/>
      <id>https://www.endpointdev.com/blog/2016/03/story-telling-with-cesium/</id>
      <published>2016-03-07T00:00:00+00:00</published>
      <author>
        <name>Dmitry Kiselev</name>
      </author>
      <content type="html">
        &lt;h3 id=&#34;let-me-tell-you-about-my-own-town&#34;&gt;Let me tell you about my own town&lt;/h3&gt;
&lt;p&gt;I was born in Yekaterinburg. It’s a middle-sized town in Russia.&lt;/p&gt;
&lt;p&gt;Most likely you don’t know where it is. So let me show you:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-html&#34; data-lang=&#34;html&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#c00;font-weight:bold&#34;&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;html&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;lang&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;title&lt;/span&gt;&amp;gt;Hello World!&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;title&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;src&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;/cesium/Build/Cesium/Cesium.js&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;link&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;rel&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;stylesheet&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;href&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;layout.css&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;link&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;head&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt; &lt;span style=&#34;color:#369&#34;&gt;id&lt;/span&gt;=&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#34;cesiumContainer&amp;#34;&lt;/span&gt;&amp;gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;div&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; viewer = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Cesium.Viewer(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;cesiumContainer&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(){
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; ekb = viewer.entities.add({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          name : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Yekaterinburg&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#888&#34;&gt;// Lon, Lat coordinates
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;          position : Cesium.Cartesian3.fromDegrees(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;60.6054&lt;/span&gt;, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;56.8389&lt;/span&gt;),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#888&#34;&gt;// Styled geometry
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;          point : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pixelSize : &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;5&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          color : Cesium.Color.RED
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          },
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#888&#34;&gt;// Labeling
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;          label : {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            text : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;Yekaterinburg&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            font : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;16pt monospace&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            style: Cesium.LabelStyle.FILL_AND_OUTLINE,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            outlineWidth : &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;2&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            verticalOrigin : Cesium.VerticalOrigin.BOTTOM,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            pixelOffset : &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Cesium.Cartesian2(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;, -&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;9&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        });
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// How to place camera around point
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; heading = Cesium.&lt;span style=&#34;color:#038&#34;&gt;Math&lt;/span&gt;.toRadians(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; pitch = Cesium.&lt;span style=&#34;color:#038&#34;&gt;Math&lt;/span&gt;.toRadians(-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;30&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        viewer.zoomTo(ekb, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Cesium.HeadingPitchRange(heading, pitch, &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;10000&lt;/span&gt;));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    })();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;script&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;body&lt;/span&gt;&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&amp;lt;/&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;html&lt;/span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: left;&#34;&gt;&lt;a href=&#34;/blog/2016/03/story-telling-with-cesium/image-0-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/03/story-telling-with-cesium/image-0.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Now, I would like to tell you about the history of my town. In the beginning it was a fortified metallurgical plant with a few residential blocks and some public buildings. It was relatively small.&lt;/p&gt;
&lt;p&gt;I could say that its size was about the size of a modern city-center, but that description is too vague.  I think the best way to tell you something about the city is with a map.&lt;/p&gt;
&lt;p&gt;I’ll show you two maps.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A map from 1750, early in the town’s history when it was just a factory: &lt;a href=&#34;http://www.retromap.ru/m.php#r=1417506&#34;&gt;http://www.retromap.ru/m.php#r=1417506&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A map from 1924 at about the start of Soviet industrialization, just before Yekaterinburg became the industrial center of the Urals: &lt;a href=&#34;http://www.retromap.ru/m.php#r=1419241&#34;&gt;http://www.retromap.ru/m.php#r=1419241&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Thanks for these beautiful maps to &lt;a href=&#34;http://www.retromap.ru/&#34;&gt;http://www.retromap.ru&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The map from 1750 is a small image, so I’ve added it via &lt;a href=&#34;http://cesiumjs.org/Cesium/Build/Documentation/SingleTileImageryProvider.html&#34;&gt;SingleTileImageryProvider&lt;/a&gt;. Just specifying the src and coordinates:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt; setupImagery() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; layers = viewer.scene.imageryLayers;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; s = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;56.8321929&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; n = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;56.8442609&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; w = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;60.5878970&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; e = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;60.6187892&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; l1750 = layers.addImageryProvider(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Cesium.SingleTileImageryProvider({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        url : &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;assets/1750.png&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        rectangle : Cesium.Rectangle.fromDegrees(w,s,e,n)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    l1750.alpha = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0.75&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now you can see how small my town was initially.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: left;&#34;&gt;&lt;a href=&#34;/blog/2016/03/story-telling-with-cesium/image-1-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/03/story-telling-with-cesium/image-1.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;The second image is larger, so I’ve split it up into tiles. If you use &lt;a href=&#34;http://www.gdal.org/gdal2tiles.html&#34;&gt;gdal2tiles.py&lt;/a&gt; for generating tiles, it creates all the metadata necessary for &lt;a href=&#34;http://wiki.osgeo.org/wiki/Tile_Map_Service_Specification&#34;&gt;TMS&lt;/a&gt; and you are able connect the imagery set via &lt;a href=&#34;https://cesiumjs.org/releases/1.17/Build/Documentation/TileMapServiceImageryProvider.html&#34;&gt;TileMapServiceImageryProvider&lt;/a&gt;. Otherwise, you can use &lt;a href=&#34;http://cesiumjs.org/Cesium/Build/Documentation/UrlTemplateImageryProvider.html&#34;&gt;UrlTemplateImageryProvider&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; l1924 = layers.addImageryProvider(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Cesium.TileMapServiceImageryProvider({
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    url: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;assets/1924t&amp;#39;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    minimumLevel: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;8&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    maximumLevel: &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;16&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    credit: &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;retromap.ru&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}));
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;l1924.alpha = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0.75&lt;/span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I’ve used &lt;a href=&#34;http://www.qgis.org/ru/site/&#34;&gt;QGIS&lt;/a&gt; for geo referencing. Here is a good &lt;a href=&#34;http://qgis.spatialthoughts.com/2012/02/tutorial-georeferencing-topo-sheets.html&#34;&gt;tutorial&lt;/a&gt;.&lt;/p&gt;
&lt;h4 id=&#34;user-interface-and-angularjs&#34;&gt;User interface and Angular.js&lt;/h4&gt;
&lt;p&gt;And below here you see how I’ve added some controls for visibility and opacity of the overlays. Later we will bind them with an Angular-driven GUI:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;// APP is global
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; layersHash = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;l1750&amp;#39;&lt;/span&gt;: l1750,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;l1924&amp;#39;&lt;/span&gt;: l1924
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;APP.setAlpha = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(layer, alpha) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(layersHash[layer] &amp;amp;&amp;amp; layersHash[layer].alpha !== &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;undefined&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        layersHash[layer].alpha = alpha;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;APP.show = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(layer) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(layersHash[layer] &amp;amp;&amp;amp; layers.indexOf(layersHash[layer]) &amp;lt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        layers.add(layersHash[layer]);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Why not keep our views in a namespace and access them directly from an Angular controller? Using this approach will give us a lot of flexibility:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You can split up the GUI and Cesium modules and use something else instead of Cesium or Angular.&lt;/li&gt;
&lt;li&gt;You are able to make a proxy for &lt;code&gt;APP&lt;/code&gt; and add business logic to the calls made to it.&lt;/li&gt;
&lt;li&gt;It just feels right not to meld all parts of the application into one unmanageble mish-mash of code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the GUI I’ve added a big slider for the timeline, small sliders for layer opacity, checkboxes for visibility, and call APP methods via Angular’s $watch.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: left;&#34;&gt;&lt;a href=&#34;/blog/2016/03/story-telling-with-cesium/image-2-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/03/story-telling-with-cesium/image-2.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$scope.$watch(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;slider.value&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; v = $scope.slider.options.stepsArray[$scope.slider.value];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (v &amp;gt;= &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1723&lt;/span&gt; &amp;amp;&amp;amp; v &amp;lt;= &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1830&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $scope.menu.layers[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;].active = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $scope.menu.layers[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;].active = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; (v &amp;gt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1830&lt;/span&gt; &amp;amp;&amp;amp; v &amp;lt; &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1980&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $scope.menu.layers[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;].active = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $scope.menu.layers[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;].active = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;true&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(v &amp;gt;= &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1980&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $scope.menu.layers[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;].active = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        $scope.menu.layers[&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;1&lt;/span&gt;].active = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    $scope.updateLayers();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;$scope.updateLayers = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; i = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;; i &amp;lt; $scope.menu.layers.length; i++) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt; ($scope.menu.layers[i].active ) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            APP.show &amp;amp;&amp;amp; APP.show($scope.menu.layers[i].name);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            APP.hide &amp;amp;&amp;amp; APP.hide($scope.menu.layers[i].name);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4 id=&#34;back-to-the-history&#34;&gt;Back to the history&lt;/h4&gt;
&lt;p&gt;Yekaterinburg was founded on November 7, 1723. This is the date of the first test-run of the forging hammers in the new factory. The original factory design by Tatishew had 40 forging hammers and 4 blast furnaces. That may well have made it the best equipped and most productive factory of its time.&lt;/p&gt;
&lt;p&gt;Now I want to add text to the application. Also, I have some cool pictures of the hammers and furnaces. Adding an overlay for text and binding its content is a matter of &lt;em&gt;CSS&lt;/em&gt; and &lt;em&gt;ng-include/ng-bind&lt;/em&gt; knowledge and it’s a bit out of scope for this post, but let’s push on and add some pictures and link them to the timeline. Cesium has &lt;a href=&#34;http://cesiumjs.org/Cesium/Build/Documentation/KmlDataSource.html&#34;&gt;KmlDataSource&lt;/a&gt; for KML loading and parsing. This is how my application loads and accesses these attributes:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; entityByName = {};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; promise = Cesium.KmlDataSource.load(&lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;assets/foto.kml&amp;#39;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;promise.then(&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(dataSource) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    viewer.dataSources.add(dataSource);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#888&#34;&gt;//KML entities
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; entities = dataSource.entities.values;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; i = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;; i &amp;lt; entities.length; i++) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; entity = entities[i];
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#888&#34;&gt;// &amp;lt;Data&amp;gt; attributes, parsed into js object
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#888&#34;&gt;&lt;/span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; ed = entity.kml.extendedData;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        entityByName[entity.id] = {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#d20;background-color:#fff0f0&#34;&gt;&amp;#39;entity&amp;#39;&lt;/span&gt;: entity,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            since: &lt;span style=&#34;color:#038&#34;&gt;parseInt&lt;/span&gt;(ed.since.value),
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            to: &lt;span style=&#34;color:#038&#34;&gt;parseInt&lt;/span&gt;(ed.to.value)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        };
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Add bindings for Angular:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;APP.filterEtityByY = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(y) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;for&lt;/span&gt; (&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; k &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;in&lt;/span&gt; entityByName) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(entityByName.hasOwnProperty(k)) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; s = entityByName[k].since;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; t = entityByName[k].to;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            entityByName[k].entity.show = (y &amp;gt;= s &amp;amp;&amp;amp; y &amp;lt;= t);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; heading = Cesium.&lt;span style=&#34;color:#038&#34;&gt;Math&lt;/span&gt;.toRadians(&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;0&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; pitch = Cesium.&lt;span style=&#34;color:#038&#34;&gt;Math&lt;/span&gt;.toRadians(-&lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;30&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; distanceMeters = &lt;span style=&#34;color:#00d;font-weight:bold&#34;&gt;500&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;var&lt;/span&gt; enityHeading = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;new&lt;/span&gt; Cesium.HeadingPitchRange(heading, pitch, distanceMeters);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;APP.zoomToEntity = &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;function&lt;/span&gt;(name) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#080;font-weight:bold&#34;&gt;if&lt;/span&gt;(name &amp;amp;&amp;amp; entityByName[name]) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        viewer.zoomTo(entityByName[name].entity, enityHeading);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;I’ve added object timespans via extended data. If you want to use the Cesium/GE default timeline, you should do it via a &lt;em&gt;TimeSpan&lt;/em&gt; section in the KML’s entries:&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;background-color:#fff;-moz-tab-size:4;-o-tab-size:4;tab-size:4;&#34;&gt;&lt;code class=&#34;language-xml&#34; data-lang=&#34;xml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;timespan&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;begin&amp;gt;&lt;/span&gt;2000-01-00T00:00:00Z&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/begin&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;end&amp;gt;&lt;/span&gt;2000-02-00T00:00:00Z&lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/end&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#b06;font-weight:bold&#34;&gt;&amp;lt;/timespan&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Another interesting fact about my town is that between 1924 and 1991 it had a different name: &lt;em&gt;Sverdlovsk (Свердловск)&lt;/em&gt;. So I’ve added town name changing via APP and connected it to the timeline.&lt;/p&gt;
&lt;p&gt;Using &lt;em&gt;APP.filterEtityByY&lt;/em&gt; and &lt;em&gt;APP.zoomToEntity&lt;/em&gt; it’s relatively easy to connect a page hash like &lt;em&gt;example.com/#!/feature/smth&lt;/em&gt; with features from KML. One point to note is that I use my own hash’s path part parser instead of ngRoute’s approach.&lt;/p&gt;
&lt;div class=&#34;separator&#34; style=&#34;clear: both; text-align: left;&#34;&gt;&lt;a href=&#34;/blog/2016/03/story-telling-with-cesium/image-3-big.png&#34; imageanchor=&#34;1&#34; style=&#34;margin-left: 1em; margin-right: 1em;&#34;&gt;&lt;img border=&#34;0&#34; src=&#34;/blog/2016/03/story-telling-with-cesium/image-3.png&#34;/&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;You can see how all these elements work together at &lt;a href=&#34;https://dmitry.endpointdev.com/cesium/ekb/&#34;&gt;https://dmitry.endpointdev.com/cesium/ekb/&lt;/a&gt; and the sources are on GitHub: &lt;a href=&#34;https://github.com/kiselev-dv/EkbHistory/tree/master&#34;&gt;https://github.com/kiselev-dv/EkbHistory/tree/master&lt;/a&gt;&lt;/p&gt;

      </content>
    </entry>
  
</feed>
