Dynamically altering the appearance of map features

I've been trying to dynamically change the style of map elements, such as water masses, based on computed colors but haven't had any luck so far. I'm currently using Tangram for this purpose, although I'm open to exploring other engines if there's a better solution available. In Tangram, styling is usually done using a YAML file, but I'm attempting to achieve dynamic color changes by incorporating inline JavaScript in the scene (YAML) file. Instead of simply assigning a fixed color to an element like this:

 water:
    draw:
        polygons:
            color: "blue"

I'm trying something more complex like this (especially since I'm integrating Vue Router):

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

The computation of computedColors happens in a mixin, and then it's 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's how the route looks:

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);
        }

    });

If you have any insights or tips on what might be going wrong, please do share.


UPDATE

I've encountered an error related to Tangram while working on this. It seems to be connected to parsing the YAML file, but I'm struggling to pinpoint the exact issue. When I modify this part in my scene.yaml:

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

To this:

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

The errors disappear, but unfortunately, the water mass still doesn't receive any assigned color. Additionally, I needed to adjust these lines in the map route instance too:

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

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

Answer №1

This particular method does not directly address the issue of dynamically styling a Tangram map on the fly, as Yaml does not easily allow for it. However, it does offer some insight into how to style map vectors dynamically. By utilizing the plugin Leaflet.VectorGrid and programmatically assigning properties to layers using the vectorTileLayerStyles method, you can achieve this (as demonstrated in the example 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 essentially represents a GeoJson with added content like this:

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

While this solution is effective for small data sets, it may strain the browser when handling larger amounts of data due to being a client-side solution. Nonetheless, it can be handy for quickly styling a simplified world map or specific map area on-the-fly.


UPDATE

A more efficient approach involves utilizing a tile server. In the following example, we use t-rex, implementing the canvas renderer L.canvas.tile within the rendererFactory option instead of L.svg.tile, along with 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);

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can we check if this element is empty using jQuery?

If the <p></p> on the second page is empty, I want to jump from the first page to the third. Or if it's empty on the third page, then jump to the fourth. $(".lr1").click(function() { $(".p1").slideUp("fast"); $(".p2").slideDown("fas ...

Tips on saving a form submit button data to localStorage

As a newcomer, I am on a quest to make this function properly. My goal is to create a form where the submit button saves data to the localStorage. Here's what I have tried thus far. <script> function storeRecipe() { localStorage.setItem($(" ...

Which HTML element does each script correspond to?

Are there any methods to identify the script used in certain HTML elements? For instance, if I wish to determine the script responsible for creating a drop-down menu, I can locate the HTML and CSS for the menu but not the JavaScript (or other scripts). I ...

Retrieving binary content from an HTML5 Canvas using base64 encoding (readAsBinaryString)

Is it possible to extract binary data from an HTML Canvas? Currently, I have the following HTML code displaying an input file and a canvas below it: <p><button id="myButton" type="button">Get Image Content</button></p> <p>In ...

Error Observed When Binding Models in Vue.js

I've been using Vue.js for the past 5 months and it's a fantastic framework. However, I am currently facing an issue with model binding in my input. I was attempting to add a language object to my languages array at runtime but when I clear the l ...

I am a beginner in the world of MEAN stack development. Recently, I attempted to save the data from my form submissions to a MongoDB database, but unfortunately, I have encountered

const express = require('express'); const bodyParser = require('body-parser'); const mongoose = require('mongoose'); mongoose.connect('mongodb://localhost/test'); const Schema = new mongoose.Schema({ username: St ...

Incorporating CKEditor with ASP.NET: A Seamless Integration

I am currently in the process of integrating a content management system (CMS) for a website. My familiarity with Javascript, jQuery, C#, etc., is limited as I primarily work with Java, SQL, and C++. My query pertains to the CKEditor instance loaded on the ...

Encountering a problem during the installation process of cldr

GET `https://github.com/unicode-cldr/cldr-units-modern/archive/36.0.0.zip` Uh oh! There was an error while making the request. The error mentioned above occurs when attempting to: npm i cldr-data We have been using an Angular project for quite some time ...

Sharing data in JavaScript functions

In order to use certain variables in two separate functions, there are some considerations to keep in mind. The first function is responsible for calculating and displaying a conjugated verb using these variables. The second function checks the user's ...

The exceljs function for downloading an Excel workbook is not activated by using the Post method

Presently, I am engaged in the development of a React and Node application. This app is designed to store data entries in a database, with the capability for users to download all stored data in an Excel workbook. To make this possible, I have integrated ...

Is it possible to create an input field exclusively for tags using only CSS?

I am currently facing some limitations with a website I am managing. Unfortunately, I do not have the ability to incorporate additional libraries such as custom jQuery or JavaScript scripts. My goal is to customize an input field for tags so that when us ...

What is the reason behind AngularJS recognizing the $index directive and not the $parent directive?

I have been working on this up until now. Check out my code here. I'm trying to access $parent.$index in a directive, is that possible? Can someone explain it to me? <ul> <li ng-repeat="item in lists"> {{item.name}} ...

Deducing Generics in Typescript Functions Without Invocation of the Function

Take a look at the code snippet below; it seems like it should work without any issues. But for some reason, there is an error. Can anyone provide any insights into why this might be happening? Check out the TS-playground link const func_returnsInput = ( ...

Ensure the initial value in the dropdown menu is automatically set to the first value in VueJs

How can I set the first value of my time object as the default value for my dropdown in Vue? I want the first value to be pre-selected when a user enters the website, but currently only display the value without actually selecting it. time Object:- { &quo ...

Creating a TypeScript class with methods to export as an object

Just dipping my toes into Typescript and I've encountered a bit of a challenge. I have a generic class that looks like this: export class Sample { a: number; b: number; doSomething(): any { // return something } } My issue ari ...

Is there an Angular Profile service offering getter and setter properties?

Can a singleton Angular service be created with getters and setters along with logic implementation? I was provided with the following code snippet and tasked with replicating it in an Angular service. Although it may seem straightforward, I'm finding ...

What is the best method to determine the time difference between two timestamps using JavaScript?

Could you kindly show me how to calculate the difference between two times using JavaScript? I have attempted to find a solution, but I am encountering errors. Here is the code snippet from my fiddle: Visit my jsFiddle var currentTime = new ...

Error in Stripe.js: Unable to access the property 'stripeToken' because it is undefined

I'm currently in the process of integrating stripe.js into a web application that I'm developing. However, I encountered the following error: Cannot read property 'stripeToken' of undefined Although the client-side is setting the hidd ...

What is the method to set a default sorting for a v-data-table?

I'm having trouble getting the default sorting to function properly. The custom-sort argument in the documentation is described as a "Function used to sort items", but the specifics are unclear. I'm unsure if it is called for an initial sort and ...

how to adjust the width of table columns dynamically using ng-repeat and AngularJS

Working on a user interface that contains a table where the column widths adjust according to percentage data. I'm using ng-repeat to populate the table, but need help setting unique widths for each piece of data received. Here's some of my Angul ...