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:
- Select the value for
dataStyleProp
from a radio button on the HTML page, - Pass that value to the Chroma script,
- 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;
}
});
});
}
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:
map.setData()
to display it.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 evensetStyle
to update the visualisation.