Mapbox GL JS refresh layer

2019-06-07 04:04发布

问题:

I'm using Chroma.js to style a Mapbox choropleth map. I do this by storing in a variable dataStyleProp a single property of the source -- in this case the data are population and the properties are "free", "slave", "white", and "total"). Then I use dataStyleProp to build the Chroma.js color scale. What I would like to do is:

  1. Select the value for dataStyleProp from a radio button on the HTML page,
  2. Pass that value to the Chroma script,
  3. Then refresh the map to show the new choropleth.

I've managed to do 1. and 2. but can't get 3. to work. I've tried map.update and map.resize to try to force the refresh but neither works. I also looked at map.getSource(source).setData(data) but the data passed must be a geojson and my dataStyleProp is a string.

I considered creating a new layer for each dataStyleProp value, but that would mean creating hundreds of extra layers (each source has at least 4 different dataStyleProp values) and I'd like to avoid creating all those extra layers if possible.

How can I refresh the map to show the new dataStyleProp color scale?

HTML:

<div id="map"></div>
<div>
  <ul class="sidebar-list">
    <li id="dataInput"> 
      <div id="data1"></div>
      <div id="data2"></div>
      <div id="data3"></div>
      <div id="data4"></div>
      <div><button id="update">Update</button></div>
    </li>
  </ul>
</div>

JS:

mapboxgl.accessToken = "myaccesstoken";
var map = new mapboxgl.Map({
    container: "map",
    style: "mapbox://styles/mystyle",  
    center: [-100.04, 38.907],
    minZoom: 3
    });

function loadAll() {
  function getStates() {
    return fetch("data/1790_state_race.json").then(function(response) {
        return response.json();
        });
    }

  getStates().then(function(response) {
    states1790race = response;
    console.log(states1790race);
    });

all = $.when(states1790race); 

all.done(function() {  

    var vtMatchProp = "GISJOIN";
    var dataMatchProp = "GISJOIN";

    map.on("load", function () {
      map.addSource("states1790", {
            type: "vector",
            url: "mapbox://statessource"  
            });

      map.addSource("1790", {
            "type": "vector",
            "url": "mapbox://countiessource"
            });

      if (document.getElementsByName("dataInput").value === undefined) {
        var dataStyleProp = "TOTAL";
        }  
    var dataInput = document.getElementsByName("dataInput");
            $("#update").on("click", function() {
                    for (var i = 0; i < dataInput.length; i++) {
                            if (dataInput[i].checked) {
                                dataStyleProp = dataInput[i].value;
                                break;
                                }
                            }
                    console.log(dataStyleProp);

                    //refresh map layer here???

                    });

    var stops = [["0", "rgba(227,227,227,0)"]];
    var numbers1790 = states1790race.map(function(val) {
            return Number(val[dataStyleProp])
            });
    //chroma quantile scale
    var limits1790 = chroma.limits(numbers1790, 'q', 9);

    //chroma color scale
    var colorScale = chroma.scale(["#ffe8c6", "#915c0e"]).mode("lch").colors(10);

    var newData1790 = states1790race.map(function(state) {
        var color = "#e3e3e3";
        for (var i = 0; i < limits1790.length; i++) {
            if (state[dataStyleProp] <= limits1790[i]) {
              color = colorScale[i];
              break;
              }
            }
        var id = state[dataMatchProp];
        return [id, color]
    });

map.addLayer({
        "id": "1790states",
        "source": "states1790",
        "source-layer": "USA-states-1790",
        "type": "fill",
        "layout": { "visibility": "visible" }, 
            "paint": {
                "fill-color": {
                    "property": vtMatchProp,
                    "type": "categorical",
                    "stops": newData1790
                    }, 
                "fill-outline-color": "white",
                "fill-opacity": 1
                } 
            });  

});

map.on("click", function(e) {

        var features = map.queryRenderedFeatures(e.point, { layers: ["1790states", "1790counties" ] });
        map.getCanvas().style.cursor = (features.length) ? "pointer": "";

        var feature = features[0];


        if (feature.properties.COUNTY == undefined) {
            for (var i = 0; i < states1790race.length; i++) {
                if (feature.properties.GISJOIN == states1790race[i].GISJOIN) {
                    total = states1790race[i].TOTAL;
                    free = states1790race[i].FREE;
                    slave = states1790race[i].SLAVE;
                    white = states1790race[i].WHITE;
                    year = states1790race[i].YEAR;
                    document.getElementById("data1").innerHTML = "<input type='radio' checked name='dataInput' value='TOTAL'> Total State Population: " + addCommas(total);
                    document.getElementById("place").innerHTML = feature.properties.STATENAM;
                    }
                } 

            } else if 

             (feature.properties.COUNTY != undefined) {
            for (var j = 0; j < counties1790race.length; j++) {
                if (feature.properties.GISJOIN == counties1790race[j].GISJOIN) {
                    total = counties1790race[j].TOTAL;
                    free = counties1790race[j].FREE;
                    slave = counties1790race[j].SLAVE;
                    white = counties1790race[j].WHITE;
                    year = counties1790race[j].YEAR;
                    document.getElementById("data1").innerHTML = "<input type='radio' checked name='dataInput' value='TOTAL'> Total County Population: " + addCommas(total);
                    document.getElementById("place").innerHTML = feature.properties.NHGISNAM + " County, " + feature.properties.STATENAM;
                    }
                } 
            }

        if (feature.properties.STATENAM != undefined) {
            var buildings = map.querySourceFeatures(e.point);
            document.getElementById("data2").innerHTML = "<input type='radio' name='dataInput' value='FREE'> Free: " + addCommas(free);
            document.getElementById("data3").innerHTML = "<input type='radio' name='dataInput' value='SLAVE'> Slave: " + addCommas(slave);
            document.getElementById("data4").innerHTML = "<input type='radio' name='dataInput' value='WHITE'> White: " + addCommas(white);
            document.getElementById("timeline").innerHTML = "Population in " + year;
            }

    });

  });

}

回答1:

I'm a bit confused about what you're trying to do (I don't know what Chroma is), but it looks like you are trying to somehow dynamically update properties of a vector tile source hosted on Mapbox. You can't do this.

You basically have two options for a dynamic data visualisation:

  1. Generate a GeoJSON in the browser, and call map.setData() to display it.
  2. Keep the map data static, but generate a huge data-driven property to visualise it: "If state name = X, draw it pale pink. If state name = Y, draw it pale blue" etc.

If you already have all the data and all the geographies in the tileset, then I'm not clear on what your problem is, or why you have trouble "refreshing" it. Just call setPaintProperty or even setStyle to update the visualisation.