Make data layer disappear and different one appear on click in Leaflet - javascript

I'm mapping COVID vaccinations per state and per county in the MXN. I have state data as well as county data displaying properly.
I'm trying to make it so that when I click on a state it zooms in to the selected state, turns off the state data layer and turns on the county data layer. So basically it goes from showing state data to county data.
How do I do this? Would I need to add something to the zoomTofeature function?
var map = L.map("map", {
center: [24.0376106, -102.9590598],
zoom: 4.5,
});
// add basemap
var Stamen_TonerLite = L.tileLayer(
"https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
{
attribution:
'© OpenStreetMap'
maxZoom: 18,
minZoom: 3,
}
).addTo(map);
//zoom to a state, turn off state data, turn on county data
function zoomToFeature(e) {
map.fitBounds(e.target.getBounds());
}
//zoom to county
function zoomToFeature1(e) {
map.fitBounds(e.target.getBounds());
}
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 4,
opacity: 1,
color: "#dbff4d",
});
layer.bringToFront();
}
function highlightFeature1(e) {
var layer = e.target;
layer.setStyle({
weight: 4,
opacity: 1,
color: "#dbff4d",
});
layer.bringToFront();
}
//reset the hightlighted states on mouseout
function resetHighlight(e) {
geojsonStates.resetStyle(e.target);
}
//reset the hightlighted counties on mouseout
function resetHighlight1(e) {
geojsonCounties.resetStyle(e.target);
}
//add these events to the layer object
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
click: zoomToFeature,
mouseout: resetHighlight,
});
}
//add these events to the layer object
function onEachFeature1(feature, layer) {
layer.on({
mouseover: highlightFeature1,
click: zoomToFeature1,
mouseout: resetHighlight1,
});
}
function getColor(d) {
return d > 70
? "#2c7bb6"
: d >= 60
? "#abd9e9"
: d >= 50
? "#fdae61"
: d < 50
? "#d7191c"
: "#5f5f5f";
}
function getColor1(d) {
return d >= 75
? "#1a9641"
: d >= 50
? "#a6d96a"
: d >= 25
? "#fdae61"
: d > 0
? "#d7191c"
: "#5f5f5f";
}
function states(feature) {
return {
weight: 3,
opacity: 1,
color: "whitesmoke",
fillOpacity: 0.8,
fillColor: getColor(feature.properties.vaxData_states_Series_Complete),
};
}
function counties(feature) {
return {
weight: 0.5,
opacity: 0.5,
color: "whitesmoke",
fillOpacity: 0.75,
fillColor: getColor1(feature.properties.vaxData_Series_Complete),
};
}
// add state borders
var geojsonStates = L.geoJson.ajax("data/us_states.geojson", {
style: states,
onEachFeature: onEachFeature,
});
geojsonStates.addTo(map);
//add county geojson
var geojsonCounties = L.geoJson.ajax("data/counties.geojson", {
style: counties,
onEachFeature: onEachFeature1,
});
geojsonCounties.addTo(map);
I hope you can help me and I have had this issue for more than a week and I don't know what the problem could be.

Related

leaflet geojsos, updating makers with Ajax, how to remove all markers before re adding?

Im using GeoJSON with leaflet to insert markers onto a map, I then have a Ajax request periodically update the icons every 60 seconds with their latest state (they go red or green if the location is up or down)
However it's been noted that the page looked like it had a memory leak, on further investigations we can see that additional markers are added on each refresh, so with 100 markers on the map after an hour we have 6000 markers. Can anyone help me on making the existing markers update based on the new data or remove and re add them?
current code below
Thanks
<script type="text/javascript">
var map = L.map('map').setView([54.0,-3.4], 7);
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox',
maxZoom: 18,
id: 'mapbox/dark-v10',
accessToken: 'pk.*****'
}).addTo(map);
$(function() {
function update_maps() {
// Update the pins in the amaps
$.get('/monitoring/data/status_map_geo_data/gb/', function(geo_data) {
L.geoJSON(geo_data, {
pointToLayer: function (feature, latlng) {
var zindex = feature.properties.z_index && feature.properties.z_index !== "null";
return L.marker(latlng, {
zIndexOffset: zindex ? 1000 : 0,
icon: L.AwesomeMarkers.icon(
{
icon: feature.properties.icon,
markerColor: feature.properties.color,
prefix: 'fa',
iconColor: 'white',
}
)
}
);
},
onEachFeature: function (feature, layer) {
var layer_text = '<h3>'+feature.properties.popupContent+'</h3>'
layer.bindPopup(layer_text)
}
}).addTo(map);
});
}
$(document).ready(function() {
// load icons on start
update_maps()
});
// refresh page
setInterval(function() {
update_maps()
}, 60 * 1000);
});
</script>
This is a simple solution, L.geoJSON returns a group with the markers, this group can be cleared with .clearLayers().
So change your code to:
var geoJsonGroup = null;
function update_maps() {
// Update the pins in the amaps
$.get('/monitoring/data/status_map_geo_data/gb/', function(geo_data) {
if(geoJsonGroup){
geoJsonGroup.clearLayers();
}
geoJsonGroup = L.geoJSON(geo_data, {
pointToLayer: function (feature, latlng) {
var zindex = feature.properties.z_index && feature.properties.z_index !== "null";
return L.marker(latlng, {
zIndexOffset: zindex ? 1000 : 0,
icon: L.AwesomeMarkers.icon(
{
icon: feature.properties.icon,
markerColor: feature.properties.color,
prefix: 'fa',
iconColor: 'white',
}
)
}
);
},
onEachFeature: function (feature, layer) {
var layer_text = '<h3>'+feature.properties.popupContent+'</h3>'
layer.bindPopup(layer_text)
}
}).addTo(map);
});
}
Alternatives (From #gyhbs) "Many roads lead to Rome":
Call geoJsonGroup.removeFrom(map) instead of geoJsonGroup.clearLayers();
Put the L.geoJSON outside and then call addData instead of creating a new group everytime:
var geoJsonGroup = L.geoJSON(null, {
pointToLayer: function(feature, latlng) {
var zindex = feature.properties.z_index && feature.properties.z_index !== "null";
return L.marker(latlng, {
zIndexOffset: zindex ? 1000 : 0,
icon: L.AwesomeMarkers.icon({
icon: feature.properties.icon,
markerColor: feature.properties.color,
prefix: 'fa',
iconColor: 'white',
})
});
},
onEachFeature: function(feature, layer) {
var layer_text = '<h3>' + feature.properties.popupContent + '</h3>'
layer.bindPopup(layer_text)
}
}).addTo(map);
function update_maps() {
// Update the pins in the amaps
$.get('/monitoring/data/status_map_geo_data/gb/', function(geo_data) {
if (geoJsonGroup) {
geoJsonGroup.clearLayers();
}
geoJsonGroup.addData(geo_data)
});
}

Update map filtering with html form value

Alright, I don't really know where the issue in my code lies, but maybe it will be obvious to some of you.
I have an html form that includes names of all US states that correspond to numerical values in a geoJSON of all US counties. I use jquery function to get the user selected value, and JS to filter by state and only add those counties to my choropleth map.
// get value from form
var value;
$('select').on('click', function() {
value = this.value;
return value;
})
//filter by state value
var filtered = L.geoJson(counties, {
filter: function (features, layer) {
return features.properties.STATE == value
}
});
I then assign those counties a color based on the Count field (which is default gray) from the geoJSON and have listeners that highlight the county and update an info div on mouseover.
// create gradient
function getColor(d) {
return d > 1000 ? '#800026' :
d > 500 ? '#BD0026' :
d > 200 ? '#E31A1C' :
d > 100 ? '#FC4E2A' :
d > 50 ? '#FD8D3C' :
d > 20 ? '#FEB24C' :
d > 10 ? '#FED976' :
'#FFEDA0';
}
// determine fill
function style(feature) {
if (feature.properties.Count >= 1 && feature.properties.STATE == value) {
return {
fillColor: getColor(feature.properties.Count),
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
};
} else if (feature.properties.value != value && feature.properties.Count >= 1) {
return {
fillColor: '#DDD',
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
}
} else {
return {
fillColor: '#DDD',
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7
}
}
}
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 5,
color: '#666',
dashArray: '',
fillOpacity: 0.7
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
info.update(layer.feature.properties);
}
function resetHighlight(e) {
geojson.resetStyle(e.target);
info.update();
}
var geojson;
// listeners
geojson = filtered;
function zoomToFeature(e) {
map.fitBounds(e.target.getBounds());
}
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
click: zoomToFeature
});
}
geojson = L.geoJson(counties, {
style: style,
onEachFeature: onEachFeature
}).addTo(mymap);
var info = L.control();
info.onAdd = function (mymap) {
this._div = L.DomUtil.create('div', 'info'); // create info div
this.update();
return this._div;
};
// update info on hover
info.update = function (props) {
this._div.innerHTML = '<h4>2020 Sales</h4>' + (props ?
props.NAME + ' county: '
+ props.Count + ' sales / county' :
'Hover over a state/county');
};
info.addTo(mymap);
</script>
My problem is it only updates the filtered county fill color on mouseover of the county when a state is selected from the form, whereas I want to update the entirety of the filtered county's fill color on the state select. I have a bit more code I can include on request (css and some map setup js), but I think included the pertinent stuff.
Also, this is based on the leaflet choropleth example.
EDIT
Here is the google drive link: https://drive.google.com/drive/folders/1YMQZWgQb1UJga0SnXkYT50067KSz5il3?usp=sharing , let me know if there is an issue.
The code is too large to host live. Select a state from the list on the bottom left of the page, e.i. California, and you will see my problem

How to add multiple layers in leaflet and update them dynamically

I am using leaflet to draw a layer and show the Line String data in the map.But since I have a lot of LineString (40000) data it takes a lot of time to render in one layer.So I decided to split the data into seperate layers and then update the layers one by one So each layer will have less amount of data to render.
var map = L.map('map').setView([36.447488,102.463303], 12);
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=token', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'mapbox.streets'
}).addTo(map);
var options = {
position: 'topleft',
title: 'Search',
placeholder: 'enter id ',
maxResultLength: 15,
threshold: 0.5,
showInvisibleFeatures: true,
showResultFct: function(feature,container) {
props = feature.properties;
var name = L.DomUtil.create('b', null, container);
name.innerHTML = props.id;
container.appendChild(L.DomUtil.create('br', null, container));
var cat = props.id
info = '' + cat + ', ' + 'th id';
container.appendChild(document.createTextNode(info));
}
};
var searchCtrl = L.control.fuseSearch(options);
searchCtrl.addTo(map);
var geojson;
function getColor(d) {
if(d==10 || d==9 || d==8){
return '#ff0000';
}
else {
return '#00ff65';
}
}
function style(feature) {
return {
weight: 2,
opacity: 1,
color: getColor(feature.properties.points),
fillOpacity: 0.7,
};
}
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 2,
color:'#007d80',
dashArray: '',
fillOpacity: 0.7
});
if (!L.Browser.ie && !L.Browser.opera && !L.Browser.edge) {
layer.bringToFront();
}
info.update(layer.feature.properties);
}
function resetHighlight(e) {
geojson.resetStyle(e.target);
info.update();
}
function zoomToFeature(e) {
map.fitBounds(e.target.getBounds());
map.doubleClickZoom.disable();
}
var info = L.control();
info.update = function (props) {
this._div.innerHTML = '<h4><b>Link: <b></h4>' + (props ?
'<b>LineString ' + props.id + '</b><br />'
: 'Hover over a LineSting');
};
function onEachFeature(feature, layer) {
feature.layer = layer;
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
});
var popupContent =
'<b>Link ' + feature.properties.id + '</b>';
layer.bindPopup(popupContent);
feature.layer = layer;
}
function mapupdatecolor(){
$.ajax({
type: "GET",
async : false,
url: 'http:dataurl',
success: function(data) {
console.log("sucess");
for (i = 0; i<40000; i++) {
links['features'][i]['properties']['points']=data[i].points;
}
if (geojson) {
geojson.remove();
console.log("removed");
}
function picnicFilter(feature) {
if (feature.properties.points >= 9) return true
}
geojson = L.geoJson(lines, {
filter: picnicFilter,
style: style,
onEachFeature: onEachFeature
}).addTo(map);
console.log("update done");
},
error: function (xhr, ajaxOptions, thrownError) {
alert(thrownError);
},
complete: function() {
setTimeout(mapupdatecolor, 5000);
}
});
// schedule the first invocation:
}
geojson = L.geoJson(lines, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
this.update();
return this._div;
};
searchCtrl.indexFeatures(links.features, ['id']);
info.addTo(map);
setTimeout( mapupdatecolor, 5000);
So the above code is to render all the LineString initially and then after the update of the points just display the LineStrings which have points >=9.But now I would like to draw multiple layers(say 10) and then draw the whole LineStrings(40000) in those 10 layers.(400 LineStrings per layer). Will increase the speed of rendering the map?
Update:
So I tried with Leaflet.VectorGrid and it is plotting the map a bit fast but when i zoom in all the lines get invisible .
var map = L.map('map');
var canvasRenderer = L.canvas();
var cartodbAttribution = '© OpenStreetMap contributors, © CartoDB';
var positron =L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=token', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'mapbox.streets'
}).addTo(map);
function getColor(d) {
if(d==10 || d==9 || d==8){
return '#ff0000';
}
else {
return '#00ff65';
}
}
var highlight;
var clearHighlight = function() {
if (highlight) {
vectorGrid.resetFeatureStyle(highlight);
}
highlight = null;
};
var vectorGrid = L.vectorGrid.slicer(lines, {
rendererFactory: L.svg.tile,
vectorTileLayerStyles: {
sliced: function(properties, zoom) {
return {
weight: 2,
opacity: 1,
color: getColor(properties.points),
fillOpacity: 0.7
}
}
},
interactive: true,
getFeatureId: function(f) {
return f.properties.id;
}
})
.on('mouseover', function(e) {
var properties = e.layer.properties;
L.popup()
.setContent(properties.id)
.setLatLng(e.latlng)
.openOn(map);
clearHighlight();
highlight = properties.id;
var p = properties.points;
var style = {
fillColor: p === 0 ? '#800026' :
p === 1 ? '#E31A1C' :
p === 2 ? '#FEB24C' :
p === 3 ? '#B2FE4C' : '#FFEDA0',
fillOpacity: 0.5,
fillOpacity: 1,
stroke: true,
fill: true,
color: 'red',
opacity: 1,
weight: 2,
};
vectorGrid.setFeatureStyle(properties.points, style);
})
.addTo(map);
map.on('click', clearHighlight);
map.setView({ lat: 36.447488, lng: 102.463303}, 12);
Any help is appreciated

why doesn't resetStyle of leaflet work for me?

I want to draw a map with few routes drawn on it.
I want to have a dropbox with numbers 1,..,n
when an item in the dropbox is chosen, the corresponding route is highlighted on the map.
I have started using "leaflet".
why doesn't my resetStyle() return the lines to their original style?
here is my code:
document.onload = loadMap();
function loadMap() {
var map = L.map('map').setView([37.8, -96], 4);
L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token={accessToken}', {
attribution: 'Map data © OpenStreetMap contributors,CC-BY-SA, Imagery © Mapbox',
maxZoom: 18,
id: 'mapbox.streets',
accessToken: 'pk.eyJ1IjoiZW==========yJ9.3HqHQ4BMRvSPaYe8ToA7YQ'
}).addTo(map);
var marker = L.marker([51.5, -0.09]).addTo(map);
var myLines = [{
"type": "LineString",
"properties": {
"id": "1"
}
"coordinates": [
[-100, 40],
[-105, 45],
[-110, 55]
]
}, {
"type": "LineString",
"properties": {
"id": "2"
}
"coordinates": [
[-105, 40],
[-110, 45],
[-115, 55]
]
}];
var myLayer = L.geoJson().addTo(map);
myLayer.addData(myLines);
geojson = L.geoJson(myLines, {
onEachFeature: onEachFeature
}).addTo(map);
}
function highlightFeature(e) {
var layer = e.target;
layer
layer.setStyle({
weight: 25,
color: '#ff3300',
dashArray: '',
fillOpacity: 0.7
});
if (!L.Browser.ie && !L.Browser.opera) {
layer.bringToFront();
}
}
function resetHighlight(e) {
geojson.resetStyle(e.target);
layer.setStyle({
weight: 5,
color: '#0000ff',
dashArray: '',
fillOpacity: 0.7
});
}
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
// click: zoomToFeature
});
}
$('select[name="dropdown"]').change(function() {
var item = $(this).val();
alert("call the do something function on option " + item);
//how to make the chosen line highlighted ??
});
The resetStyle method of L.GeoJSON reset the given layer's style back to the style defined when initializing the L.GeoJSON layer:
Resets the given vector layer's style to the original GeoJSON style, useful for resetting style after hover events.
http://leafletjs.com/reference.html#geojson-resetstyle
Code example:
var geojsonLayer = new L.GeoJSON(geojson, {
style: function () {
return {
color: 'red'
}
},
onEachFeature: function (feature, layer) {
layer.on('mouseover', function () {
this.setStyle({
color: 'green'
});
});
layer.on('mouseout', function () {
geojsonLayer.resetStyle(this);
});
}
}).addTo(map);
Working example on Plunker: http://plnkr.co/edit/iriGMBYiFRizeXizMF06?p=preview

Add external links to leaflet polygon

I use leaflet to display geographic zone with multiple polygons on a map and I want to link on click each layer to a webpage. For example the link (http://wikipedia.com) should be in replacement of "alert("You clicked the map at " + e.latlng);"
var map = L.map('map').setView([45.7676067, 4.8351733], 6);
var cloudmade = L.tileLayer('http://{s}.tile.cloudmade.com/{key}/{styleId}/256/{z}/{x}/{y}.png', {
attribution: 'Map data © 2011 OpenStreetMap, Imagery © 2012 CloudMade',
key: 'My Key',
styleId: 22677
}).addTo(map);
// control that shows state info on hover
var info = L.control();
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info');
this.update();
return this._div;
};
info.update = function (props) {
this._div.innerHTML = '<h4>Région</h4>' + (props ?
'<b>' + props.Name + '</b><br />'
: 'Survollez une région');
};
info.addTo(map);
// get color depending on population density value
function getColor(d) {
return d > 1000 ? '#800026' :
d > 500 ? '#BD0026' :
d > 200 ? '#E31A1C' :
d > 100 ? '#FC4E2A' :
d > 50 ? '#FD8D3C' :
d > 20 ? '#FEB24C' :
d > 10 ? '#FED976' :
'#FFEDA0';
}
function style(feature) {
return {
weight: 2,
opacity: 1,
color: 'white',
dashArray: '3',
fillOpacity: 0.7,
fillColor: getColor('10')
};
}
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 3,
color: '#666',
dashArray: '',
fillOpacity: 0.7,
fillColor: getColor('50')
});
if (!L.Browser.ie && !L.Browser.opera) {
layer.bringToFront();
}
info.update(layer.feature.properties);
}
var geojson;
function resetHighlight(e) {
geojson.resetStyle(e.target);
info.update();
}
function zoomToFeature(e) {
map.fitBounds(e.target.getBounds());
}
function onMapClick(e) {
alert("You clicked the map at " + e.latlng);
}
map.on('click', onMapClick);
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
//click: zoomToFeature
click: onMapClick
});
}
geojson = L.geoJson(regions, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
</script>
Thanks for you help
You should be able to use a simple window redirect in the onMapClick function:
window.location = "http://www.google.com"
or to open in a new tab or window use window.open

Categories

Resources