Full control of SVG elements in Cesium billboards

Cesium billboards are an easy way to mark a point on the map with any image you want.  It works with one simple JSON:

viewer.entities.add({

   position: position,

   billboard: {

      image: mySVGUrl

   }

});

The problem is this – what happens when you want a more complex symbology? For instance, get some SVG from the designer and show several variations according to data (i.e. different colors for different categories).

One solution is using dataURI. It’s quite simple – you get the text of the SVG (using the textjs plugin for requirejs is one way to do it).  You then change the fill attribute to your color of choice and finally you create the data URI.

Here’s a live example, simplified to work without requirejs (the svg can be imported in any way such as ajax call):

In the code below I do the same manipulation, but this time I’m using requirejs to “import” my svg.  As mentioned above, this can be achieved in numerous ways.

define(['./module', 'text!mySVGFile1.svg', 'text!mySVGFile2.svg'], function(services, mySVGFile1, mySVGFile1){

   function cesiumRgb2Hex(rgb){
      return "#" + ((1 << 24) + (rgb.red*255 << 16) + (rgb.green*255 << 8) + rgb.blue*255).toString(16).slice(1);
   }

   function exportSVG(svg){
      var b64 = "data:image/svg+xml;base64,";

      // https://developer.mozilla.org/en/DOM/window.btoa
      b64 += btoa(svg);
      
      return b64;
   }
   
   services.value('mapBillboards', {
      /**
       *
       * @param typeOfSVG - user should know what files are preloaded...
       * @param color - a cesium color object => {red: 1, green: 0 ...}
       */
      getDataURI: function(typeOfSVG, color){
         var hexColor = cesiumRgb2Hex(color); // get the hex color to fill
         
         return exportSVG(this[type].replace('<svg', '<svg fill="' + hexColor + '"'));

      },
      mySVGFile1: mySVGFile1,
      mySVGFile2: mySVGFile2
   })
});

The actual color magic happens in the return in the getDataURI method.  I replace the ‘<svg’ with ‘<svg fill=”#123432″>’ (or whatever color we sent and converted to the hex value).

In the same way, we can replace ANY SVG element – including different <g> sections (for instance – hide/show different elements in order to create complex combinations). You can hop into the live demo and play with it.

I didn’t try it yet, but using svgElement = angular.element(svg) would enable you to better control the SVG itself.  You can turn the element back to string using XMLSerializer (new XMLSerializer()).serializeToString(svgElement));

Leave a Reply