Leaflet set marker icon on click - javascript

I'm trying to change geojson marker icon on click following various exemple found on the web but I can't get it to work, it raise this error message:
TypeError: e.target.setIcon is not a function
Bellow the snippet
var map = L.map('map',{ center: [44.1, 3.5], zoom: 10});
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { attribution: 'OpenStreetMap'}).addTo(map);
var icon = L.icon({
iconUrl: 'https://cdn1.iconfinder.com/data/icons/Map-Markers-Icons-Demo-PNG/256/Map-Marker-Marker-Outside-Azure.png',
iconSize: [ 48, 48 ],
});
var data = {
"type": "FeatureCollection",
"name": "test",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "NUM": 1 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ 3.75, 44.25 ] ] } },
{ "type": "Feature", "properties": { "NUM": 2 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ 3.25, 44.0 ] ] } }
]}
L.geoJson(data,{
pointToLayer: function (feature, latlng) {
return L.marker(latlng);//, {icon: icon});
},
onEachFeature: function (feature, layer) {
layer.on('click', function (e){
e.target.setIcon(icon);
})
}
}).addTo(map);
what's wrong?

The issue is originating from the GeoJSON. It does seem to be a valid GeoJSON because it plots on the map, but it must be tripping up Leaflet somehow. I think the fact that it's a MultiPoint feature is the cause.
Changing the GeoJSON to:
var data = {
"type": "FeatureCollection",
"name": "test",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:OGC:1.3:CRS84" } },
"features": [
{ "type": "Feature", "properties": { "NUM": 1 }, "geometry": { "type": "Point", "coordinates": [ 3.75, 44.25 ] } },
{ "type": "Feature", "properties": { "NUM": 2 }, "geometry": { "type": "Point", "coordinates": [ 3.25, 44.0 ] } }
]}
makes it work. (Note: I just changed "type" from MultiPoint to Point and removed the double brackets, i.e. [ [ 3.25, 44.0 ] ] became[ 3.25, 44.0 ].)
I'm not sure if it's possible to use MultiPoint geometry with the onEachFeature function, but it seems to be working with single point geometry.

Related

How to simultaneously add new markers and remove old markers associated with polygon clicks in leaflet

I have a map with Geojson polygons where each polygon is identified by a unique ID. I have a separate Geojson of point markers where each point is associated with a polygon with the same polygon ID, as well as a unique identifier for each individual point.
The initial map display is of only polygons. Each polygon click will display the points that are associated with the clicked polygon. When a new polygon is clicked, I want to plot the points associated with the new polygon while simultaneously removing the previously plotted points. So each time the user clicks a polygon, ONLY points within that polygon are ever displayed.
Here is some minimally reproducible code: https://jsfiddle.net/3v7hd2vx/1146/
var polys = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"stroke": "#555555",
"stroke-width": 2,
"stroke-opacity": 1,
"fill": "#555555",
"fill-opacity": 0.5,
"polygon": "one"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-87.33032226562499,
37.90953361677018
],
[
-85.374755859375,
37.90953361677018
],
[
-85.374755859375,
39.036252959636606
],
[
-87.33032226562499,
39.036252959636606
],
[
-87.33032226562499,
37.90953361677018
]
]
]
}
},
{
"type": "Feature",
"properties": {
"stroke": "#555555",
"stroke-width": 2,
"stroke-opacity": 1,
"fill": "#555555",
"fill-opacity": 0.5,
"polygon": "two"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-85.3857421875,
37.90953361677018
],
[
-83.3642578125,
37.90953361677018
],
[
-83.3642578125,
39.04478604850143
],
[
-85.3857421875,
39.04478604850143
],
[
-85.3857421875,
37.90953361677018
]
]
]
}
},
{
"type": "Feature",
"properties": {
"stroke": "#555555",
"stroke-width": 2,
"stroke-opacity": 1,
"fill": "#555555",
"fill-opacity": 0.5,
"polygon": "three"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-87.34130859375,
36.90597988519294
],
[
-83.3642578125,
36.90597988519294
],
[
-83.3642578125,
37.91820111976663
],
[
-87.34130859375,
37.91820111976663
],
[
-87.34130859375,
36.90597988519294
]
]
]
}
}
]
};
var pts = {
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "one",
"animal": "bear"
},
"geometry": {
"type": "Point",
"coordinates": [
-86.72607421875,
38.77121637244273
]
}
},
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "one",
"animal": "fish"
},
"geometry": {
"type": "Point",
"coordinates": [
-86.583251953125,
38.487994609214795
]
}
},
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "two",
"animal": "tiger"
},
"geometry": {
"type": "Point",
"coordinates": [
-84.276123046875,
38.38472766885085
]
}
},
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "two",
"animal": "lizard"
},
"geometry": {
"type": "Point",
"coordinates": [
-85.067138671875,
38.70265930723801
]
}
},
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "three",
"animal": "dog"
},
"geometry": {
"type": "Point",
"coordinates": [
-83.880615234375,
37.483576550426996
]
}
},
{
"type": "Feature",
"properties": {
"marker-color": "#7e7e7e",
"marker-size": "medium",
"marker-symbol": "",
"id": "three",
"animal": "horse"
},
"geometry": {
"type": "Point",
"coordinates": [
-85.93505859374999,
37.709899354855125
]
}
}
]
};
var map = L.map("map", {
center: [37.5, -85],
zoom: 7
});
L.tileLayer("http://{s}.tile.osm.org/{z}/{x}/{y}.png").addTo(map);
var defaultStyle = {
fillColor: "whitesmoke",
color: "#171717",
fillOpacity: 1,
weight: 1
};
// create polygon geojson, add to map
var polyJson = L.geoJson(polys, defaultStyle).addTo(map);
// empty layer group to add markers to
var layerGroup = L.layerGroup();
// loop through polygon layers for events
polyJson.eachLayer(function(layer){
// zoom to bbox on each polygon click
layer.on("click", function(e){
var bbox = e.target.getBounds();
var sw = bbox.getSouthWest();
var ne = bbox.getNorthEast();
map.fitBounds([sw, ne]);
// the clicked polygon identifier
var clickedPoly = e.target.feature.properties.polygon;
// pts added to map when they match the clickedpoly id
L.geoJson(pts, {
pointToLayer: function(feature, latlng){
if(feature.properties.id === clickedPoly){
var clicked = L.circleMarker(latlng, {
color: "red"
}).addTo(map).addTo(layerGroup);
}
}
});
});
});
polyJson.on("click", function(){
if(map.hasLayer(layerGroup)){
map.removeLayer(layerGroup);
}else{
map.addLayer(layerGroup);
//layerGroup.removeLayers();
}
});
In this example, the entire layerGroup of points is removed every other click based on the conditional statement at the end of the code block. But each new click continues to append markers to the layerGroup, so ALL selected markers are displayed every other click.
My inclination is to have each "group" of markers named by the polygon that they lie within, so when displayed markers do not match the currently clicked polygon ID, they will be removed both from the map AND from the layerGroup. I've worked extensively with Leaflet in R, but I'm not sure how to accomplish this in JS (or if the logic is even the same in the different languages). I can't manually define each layer because I'll be iterating through many dynamically loaded polygons and points.
One simple way to fill your layer with teh desired points would be to:
make your layerGroup a permanent layer,
empty this layer when the user clicks on a polygon with layerGroup.clearLayers()
fill layerGroup with the valid markers (determined by the filter property) and let Leaflet handle the markers without interfering with pointToLayer.
Basically, simplify things:).
For example:
layer.on("click", function(e){
var bbox = e.target.getBounds();
var sw = bbox.getSouthWest();
var ne = bbox.getNorthEast();
map.fitBounds([sw, ne]);
// the clicked polygon identifier
var clickedPoly = e.target.feature.properties.polygon;
layerGroup.clearLayers();
layerGroup.addLayer(L.geoJson(pts, {
pointToLayer: function(feature, latlng){
return L.circleMarker(latlng, {
color: "red"
});
},
filter: function (feature) {
return feature.properties.id === clickedPoly;
}
}));
});
And a demo https://jsfiddle.net/3v7hd2vx/1147/

Mapbox: It renders the last point instead of all

I'm trying to render multiple points using Mapbox but it only displays the last one. I've added the feature parameter but nothing. This should render the two markers but i don't know why it doesn't. I'm not getting any errors in the console.
I'm stuck and can't find a way to solve it. Help?
This is the code responsible for the points.:
map.on('load', function() {
map.loadImage('images/celltower.png', function(error, image) {
if (error) throw error;
map.addImage('tower', image);
map.addLayer({
"id": "points",
"type": "symbol",
"source": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [21.42559803007430, 42.00038270989050]
},
"geometry": {
"type": "Point",
"coordinates": [21.38529272846381, 42.0080397578202]
}
}]
}
},
"layout": {
"icon-image": "tower",
"icon-size": 0.25
}
});
});
});
Thanks :)
You have a duplicate geometry key. Judging by the fact that features is an array, I would guess this is the proper way to do it:
map.on('load', function() {
map.loadImage('images/celltower.png', function(error, image) {
if (error) throw error;
map.addImage('tower', image);
map.addLayer({
"id": "points",
"type": "symbol",
"source": {
"type": "geojson",
"data": {
"type": "FeatureCollection",
"features": [{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [21.42559803007430, 42.00038270989050]
}
}, {
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [21.38529272846381, 42.0080397578202]
}
}]
}
},
"layout": {
"icon-image": "tower",
"icon-size": 0.25
}
});
});
});
According to the GeoJSON specification, there should be a way to specify multiple points. E.g.:
"features": [{
"type": "Feature",
"geometry": {
"type": "MultiPoint",
"coordinates": [
[21.42559803007430, 42.00038270989050],
[21.38529272846381, 42.0080397578202]
]
}
}]

Duplicating Point Layer for Simulation

I'm trying to simulate city traffic movements (at the moment I'm only into vehicles) but I'm having a problem.
What happens is that I'm trying to simulate 1 car per point at the map, but I don't know how to duplicate a certain layer (and each one has a different route), for example this one:
map.addSource('point', {
"type": "geojson",
"data": pointOnCircle(0)
});
map.addLayer({
"id": "point",
"source": "point",
"type": "circle",
"paint": {
"circle-radius": 10,
"circle-color": "#007cbf"
}
});
I don't know if I could just loop and generate N points with different names or do it in another way.
Here's a video of what I've done until now (for simulating it I created 2 different layers because I didn't know how to duplicate them) :
https://www.youtube.com/watch?v=xWZD9aBUFlg
You should put all your points in the one data layer:
map.addSource('points', {
"type": "geojson",
"data": pointsOnCircle(0) // change this function to return multiple features
});
map.addLayer({
"id": "points",
"source": "points",
"type": "circle",
"paint": {
"circle-radius": 10,
"circle-color": "#007cbf" // possibly make this a data-driven function
}
});
Your GeoJSON data source would look like:
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
146.25,
-37.16031654673676
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
138.515625,
35.460669951495305
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Point",
"coordinates": [
-81.5625,
33.43144133557529
]
}
}
]
}

Not showing the id in the screen when hovering

I am using leaflet and mapbox and I use the function to print the id nuber on the screen when hovering over the line in the map.
geojson = L.geoJson(lines, {
style: style,
onEachFeature: onEachFeature
}).addTo(map);
var info = L.control();
info.onAdd = function (map) {
this._div = L.DomUtil.create('div', 'info'); // create a div with a class "info"
this.update();
return this._div;
};
info.update = function (props) {
this._div.innerHTML = '<h4><b>Links<b></h4>' + (props ?
'<b>Link' + props.id + '</b><br />'
: 'Hover over a link');
};
info.addTo(map);
The way I draw the line is by the following geoJSON file
var lines = {
"type": "FeatureCollection",
"features": [{
"type": "LineString",
"coordinates": [
[103.85, 1.28992],
[103.89, 1.294],
[103.83, 1.216]
],
"properties": { "id": "1" }
}, {
"type": "LineString",
"properties": { "id": "2" },
"coordinates": [
[103.5, 1.292],
[103.9, 1.4],
[103.3, 1.21]
]
}, {
"type": "LineString",
"properties": { "id": "3" },
"coordinates": [
[103.6, 1.291],
[103.6, 1.39],
[103.3, 1.29]
]
}]
};
but when i hover over the line it shows "undefined" instead of showing the id.Any help is appreciated
Where is the declaration of your onEachFeature? Where is update called from? Your code is incomplete.
Besides, your GeoJSON is non-conformant to the GeoJSON specifications. A FeatureCollection must contain Features, and each Feature must have a Geometry.
So instead of:
{
"type": "LineString",
"properties": { "id": "3" },
"coordinates": [[103.6,1.291],[103.6,1.39],[103.3,1.29]]
}
it should be something like:
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [[103.6,1.291],[103.6,1.39],[103.3,1.29]]
},
"properties": {
"id": "3"
},
}

How to add Markers on Map from geoJson with many points (Leaflet)

I'm trying to add some markers on my map using a geoJson with Points, I'm following the leaflet documentation but it still saying:
Error: Invalid GeoJSON object.
throw new Error('Invalid GeoJSON object.');
My GeoJson:
var meta1nJson={
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-38.3613558,
-8.8044875
]
},
"properties": {
"Ordem": "193",
"Eixo": "Leste",
"Meta": "1L",
"Municipio": "Petrolândia",
"Estado": "PE",
"Nome da Comunidade": "Agrovila 4"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-38.3445892,
-8.7940031
]
},
"properties": {
"Ordem": "194",
"Eixo": "Leste",
"Meta": "1L",
"Municipio": "Petrolândia / Floresta",
"Estado": "PE",
"Nome da Comunidade": "Agrovila 5"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-37.8521847,
-8.6774657
]
},
"properties": {
"Ordem": "195",
"Eixo": "Leste",
"Meta": "1L",
"Municipio": "Inajá/Ibimirim",
"Estado": "PE",
"Nome da Comunidade": "Indígena Kambiwá - Baxa da Alexandra"
}
},
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [
-37.9229577,
-8.645232
]
},
"properties": {
"Ordem": "196",
"Eixo": "Leste",
"Meta": "1L",
"Municipio": "Inajá",
"Estado": "PE",
"Nome da Comunidade": "Indígena Kambiwá - Barracão"
}
}
]
};
and how do I try to render markers:
var layerComunidades1N = L.geoJson(meta1nJson).addTo(map);
I cannot find what I'm doing wrong, according to leaflet documentation if I don't pass the option pointToLayer it is supposed to render default markers, am I wrong?
I don't see any problem with your geojson. I copied your geojson into geojson.io and it works fine.
It does look like you're calling GeoJSON incorrectly (your example above shows a call to geoJson), however that doesn't explain the error you're getting...
Regardless, here is a working jsfiddle that visualizes your geojson as markers on a leaflet map.
And here is the relevant code (with a base layer for good measure):
var map = L.map('map', {
center: [-9, -38],
zoom: 7
});
L.tileLayer('https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png', {
id: 'examples.map-20v6611k'
}).addTo(map);
new L.GeoJSON(meta1nJson).addTo(map);
Hopefully, that helps.
Try this:
/* Instantiate an icon. You can have multiple icons also*/
var ComunidadeIcon = L.icon({
iconUrl: 'http://iambrony.dget.cc/mlp/gif/angel_stand_right.gif',
iconSize: [32, 37],
iconAnchor: [16, 37],
popupAnchor: [0, -28]
});
/* List objects features from FeatureCollection from GJson*/
var layerComunidades1N = L.geoJson(meta1nJson, {
pointToLayer: function (feature, latlng) {
return L.marker(latlng, {icon: ComunidadeIcon});
}, onEachFeature: onEachFeature
}).addTo(map);
/* This function can hold many things. I'm using to show a popup*/
function onEachFeature(feature, layer) {
var popupContent = "<p>DaTerra Web</p>";
if (feature.properties && feature.properties.popupContent) {
popupContent += feature.properties.popupContent;
}
layer.bindPopup(popupContent);
}

Categories

Resources