javascriptvue.jsleafletvue-routertangram

Changing style of map elements dynamically


I am attempting to change the style of a number of map elements on the fly, without success. By on the fly I mean changing the color of a map element, the water mass for instance, dynamically from a computed color. Using Tangram but open to other engines if there is a better solution. Since Tangram uses a YAML file to style the various elements, I am using inline Javascript on the scene (YAML) file. In other words, instead of giving it a fixed color, I want to compute the color (from an image). Instead of this:

 water:
    draw:
        polygons:
            color: "blue"

I am doing something along these lines (further complicated because I am using Vue Router):

 water:
    draw:
        polygons:
            color: function() { return this.colors[0]; }

computedColors is computed in a mixing, then broadcasted to the appropriate route:

var colorize = {

    data: function () {
        return {
            computedColors: []
        };
    },

    created: function () {
        this.getColors();
    },

    methods: {
        getColors: function () {
            ...
            self.computedColors = [
              ...
            ];
          self.$broadcast('parent-colors', self.computedColors);
          ...
        };
    };
}

Here is the route:

router.map({
'/map': {

    name: 'map',

    component: Vue.extend({

        template: '#map',

        mixins: [colorize],

        data: function () {
            return {
                colors: []
            };
        },

        events: {
            'parent-colors': function (computedColors) {
                this.colors = computedColors;
            }
        },
    ready: {
        var map = L.map('map');
        var layer = Tangram.leafletLayer({
           scene: './tiles/scene.yaml'
        });
        layer.addTo(map);
        map.setView([40.70531887544228, -74.00976419448853], 15);
    }
});

Any hints on what I may be doing wrong appreciated.


UPDATE

I am getting an error of sorts. It is related with Tangram but not quite able to figure out what it is exactly. Seems to be an issue with the parsing of the YAML file. If I change this in my scene.yaml:

 water:
    draw:
        polygons:
            color: function() { return this.colors[0]; }

With this:

 water:
    draw:
        polygons:
            color: function() { return this.color1; }

I get no errors but unfortunately the water mass still isn't assigned any color either.

Of course, I had to change these lines in the map route instance too:

 data: function () {
            return {
                color1: ''
            };
        },
 ...

 events: {
     'parent-colors': function (computedColors) {
         this.color1 = computedColors[0];
     }
 }

Solution

  • The following doesn't offer a solution to the specific problem of styling a Tangram map on the fly (which Yaml doesn't seem to easily allow), but it partially answers the question of how to style map vectors dynamically. It implements the plugin Leaflet.VectorGrid and assigns properties to layers programmatically through the vectorTileLayerStyles method (accomplished in the example below with color: self.colors[6]).

            L.vectorGrid.slicer(countries, {
            rendererFactory: L.svg.tile,
            vectorTileLayerStyles: {
                sliced: function() {
                    return {
                    stroke: true,
                    color: self.colors[6],
                    weight: 0.5,
                    };
                }
            },
            }).addTo(map);
    

    The variable countries is really just a GeoJson with var countries = added to it, something along these lines:

    var countries = {
        "type": "FeatureCollection",
        "features": [
            { "type": "Feature"
            (...)
            }
        ]
    };
    

    It's a straightforward solution and it works perfectly with small amounts of data, but since it's a client-side solution it's heavy on the browser when dealing with bigger data sets. Still, it may be useful for those who may be looking for a simple way to style a simplified world map or a limited map area on the fly.


    UPDATE

    A more performant solution is to use a tile server. The example below implements t-rex, with canvas renderer L.canvas.tile in the rendererFactory option instead of L.svg.tile and protobuf:

      var lines = "http://127.0.0.1:6767/lines/{z}/{x}/{y}.pbf";
      var multilinestrings = "http://127.0.0.1:6767/multilinestrings/{z}/{x}/{y}.pbf";
      var multipolygons = "http://127.0.0.1:6767/multipolygons/{z}/{x}/{y}.pbf";
    
      var vectorTileStyling = {
          lines: {
              weight: .8,
              fillColor: this.colors[1],
              color: this.colors[1],
              fillOpacity: .8,
              opacity: .8
          },
          multilinestrings: {
              weight: .8,
              fillColor: this.colors[2],
              color: this.colors[2],
              fillOpacity: .8,
              opacity: .8
          },
          multipolygons: {
              fill: true,
              weight: .8,
              fillColor: this.colors[3],
              color: this.colors[3],
              fillOpacity: .8,
              opacity: .8,
          }
      };
    
      var externalVectorTileOptions = {
          rendererFactory: L.canvas.tile,
          vectorTileLayerStyles: vectorTileStyling,
          maxZoom: 16,
          minZoom: 14
      };
      L.vectorGrid.protobuf(lines, externalVectorTileOptions).addTo(map);
      L.vectorGrid.protobuf(multilinestrings, externalVectorTileOptions).addTo(map);
      L.vectorGrid.protobuf(multipolygons, externalVectorTileOptions).addTo(map);