changing the color of polylines and marker icon's when the layer changes? - leaflet

Let's say I'm a purple polyline and am using a purple icon for markers with one layer but wanted to use orange polylines and orange marker icon's for another layer. How would I do that? Is there an onlayerchange event? But even if there were how could I change all the icon's for all the markers? Alternatively, maybe I could delete all the markers and then replace them, albeit with a different icon, but idk how to delete markers, en masse or otherwise.
Any ideas?

I am not sure if I understood correctly but here is what you can do.
If you want to toggle between markers with polylines and assign different color you can use this plugin and return icon markers by passing the color.
const icon = (color) => L.icon({
iconSize: [25, 41],
iconAnchor: [10, 41],
popupAnchor: [2, -40],
iconUrl: `https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-${color}.png`,
shadowUrl: "https://unpkg.com/leaflet#1.6/dist/images/marker-shadow.png"
});
and then you have the latlngs and assing to the markers an icon with the prefered color
var places1 = [
{ latlng: [39.61, -105.02], popup: 'This is Littleton, CO.'},
{ latlng: [39.74, -104.99], popup: 'This is Denver, CO.'},
{latlng: [39.73, -104.8], popup: 'This is Aurora, CO.'}
];
places1.forEach(place => L.marker(place.latlng, {
icon: icon('violet')
}).bindPopup(place.popup).addTo(cities1))
and here define the polyline color
L.polyline(places1.map(({latlng}) => latlng), {
color: 'purple'
}).addTo(cities1);
Similarly you can follow the same steps for any other overlay
<!DOCTYPE html>
<html>
<head>
<title>Layers Control Tutorial - Leaflet</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="docs/images/favicon.ico" />
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<style>
html,
body {
height: 100%;
margin: 0;
}
#map {
width: 600px;
height: 400px;
}
</style>
</head>
<body>
<div id='map'></div>
<script>
var cities1 = L.layerGroup();
var cities2 = L.layerGroup();
var map = L.map('map', {
center: [39.73, -104.99],
zoom: 10,
});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
const icon = (color) => L.icon({
iconSize: [25, 41],
iconAnchor: [10, 41],
popupAnchor: [2, -40],
iconUrl: `https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-${color}.png`,
shadowUrl: "https://unpkg.com/leaflet#1.6/dist/images/marker-shadow.png"
});
var places1 = [{
latlng: [39.61, -105.02],
popup: 'This is Littleton, CO.'
},
{
latlng: [39.74, -104.99],
popup: 'This is Denver, CO.'
},
{
latlng: [39.73, -104.8],
popup: 'This is Aurora, CO.'
}
];
places1.forEach(place => L.marker(place.latlng, {
icon: icon('violet')
}).bindPopup(place.popup).addTo(cities1))
var places2 = [{
latlng: [39.77, -105.23],
popup: 'This is Golden, CO.'
},
{
latlng: [39.75, -105.16],
popup: 'This is Applewood, CO.'
}
];
places2.forEach(place => L.marker(place.latlng, {
icon: icon('orange')
}).bindPopup(place.popup).addTo(cities2))
L.polyline(places1.map(({
latlng
}) => latlng), {
color: 'purple'
}).addTo(cities1);
L.polyline(places2.map(({
latlng
}) => latlng), {
color: 'orange'
}).addTo(cities2);
var overlays = {
"cities1": cities1.addTo(map),
"cities2": cities2
};
L.control.layers(null, overlays).addTo(map);
</script>
</body>
</html>
For the scenario you want to change the color upon baselayer change:
you can still reuse icon() function and use now the follow chunk to change the color dynamically when the layer is changed by listening to map's baselayerchange event
function addMarkersAndPolyline(color) {
places.forEach(place => L.marker(place.latlng, {
icon: icon(color)
}).bindPopup(place.popup).addTo(cities))
L.polyline(places.map(({
latlng
}) => latlng), {
color
}).addTo(cities);
}
<!DOCTYPE html>
<html>
<head>
<title>Layers Control Tutorial - Leaflet</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="docs/images/favicon.ico" />
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<style>
html,
body {
height: 100%;
margin: 0;
}
#map {
width: 600px;
height: 400px;
}
</style>
</head>
<body>
<div id='map'></div>
<script>
var cities = L.layerGroup();
var map = L.map('map', {
center: [39.73, -104.99],
zoom: 10,
});
var mbAttr = 'Map data © OpenStreetMap contributors, ' +
'Imagery © Mapbox',
mbUrl = 'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw';
var grayscale = L.tileLayer(mbUrl, {
id: 'mapbox/light-v9',
tileSize: 512,
zoomOffset: -1,
attribution: mbAttr
}),
streets = L.tileLayer(mbUrl, {
id: 'mapbox/streets-v11',
tileSize: 512,
zoomOffset: -1,
attribution: mbAttr
});
var baseLayers = {
"Grayscale": grayscale.addTo(map),
"Streets": streets
};
const icon = (color) => L.icon({
iconSize: [25, 41],
iconAnchor: [10, 41],
popupAnchor: [2, -40],
iconUrl: `https://raw.githubusercontent.com/pointhi/leaflet-color-markers/master/img/marker-icon-2x-${color}.png`,
shadowUrl: "https://unpkg.com/leaflet#1.6/dist/images/marker-shadow.png"
});
var places = [{
latlng: [39.61, -105.02],
popup: 'This is Littleton, CO.'
},
{
latlng: [39.74, -104.99],
popup: 'This is Denver, CO.'
},
{
latlng: [39.73, -104.8],
popup: 'This is Aurora, CO.'
}
];
function addMarkersAndPolyline(color) {
places.forEach(place => L.marker(place.latlng, {
icon: icon(color)
}).bindPopup(place.popup).addTo(cities))
L.polyline(places.map(({
latlng
}) => latlng), {
color
}).addTo(cities);
}
addMarkersAndPolyline('violet')
map.on('baselayerchange', function(e) {
cities.clearLayers();
if (e.name === 'Streets') {
addMarkersAndPolyline('orange');
return
}
addMarkersAndPolyline('violet');
});
var overlays = {
"cities1": cities.addTo(map),
};
L.control.layers(baseLayers, overlays).addTo(map);
</script>
</body>
</html>

Related

how to apply 'generateId:true' on composite source features?

I want to find out buildings inside a runtime-drawed polygon.
And fill with green color.
I try to archive it with feature state and case expression. When I find out those features that inside the polygon in some way, I still can't execute the map.setFeatureState, cause the first parameter need (feature object) need a specified id, however, the feature that created by composite source hasn't set id, also looks there is no way to set 'generateId:true'.
Is there any alternate way to achieve the purpose?
in the follow snippet,there is a polygon draw tool on the top-right,draw a polygon and double click to end of draw, then you will find the error alerted in the console.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Static Template</title>
<script src="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.js"></script>
<link href="https://api.mapbox.com/mapbox-gl-js/v2.1.1/mapbox-gl.css" rel="stylesheet" />
<script src="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.2.0/mapbox-gl-draw.js"></script>
<script src="https://cdn.jsdelivr.net/npm/#turf/turf#5/turf.min.js"></script>
<link rel="stylesheet" href="https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-draw/v1.2.0/mapbox-gl-draw.css" type="text/css" />
<style>
body {
margin: 0;
padding: 0;
}
#map {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
mapboxgl.accessToken =
"pk.eyJ1IjoibWluem9qaWFuIiwiYSI6ImNrbGFsem92MjAxaHAycG1sbGg3MXFsODAifQ.Kclz1IBxyU0iDiVgIjhSYQ";
var map = new mapboxgl.Map({
container: "map",
style: "mapbox://styles/mapbox/streets-v11"
});
var map = new mapboxgl.Map({
style: "mapbox://styles/mapbox/light-v10",
center: [-73.991, 40.735],
zoom: 15.5,
pitch: 45,
bearing: -17.6,
container: "map",
antialias: true
});
var draw = new MapboxDraw({
displayControlsDefault: false,
controls: {
polygon: true,
trash: true
}
});
map.addControl(draw);
map.on("draw.create", updateArea);
map.on("draw.delete", updateArea);
map.on("draw.update", updateArea);
function updateArea(e) {
if (map.getLayer("maine")) map.removeLayer("maine");
if (map.getSource("data-area")) map.removeSource("data-area");
map.addSource("data-area", {
type: "geojson",
data: draw.getAll()
});
//get features in polygon
lastInAreaFeatures = map
.queryRenderedFeatures({
layers: ["3d-buildings"]
})
.filter(function(t) {
return turf.booleanContains(
turf.polygon(draw.getAll().features[0].geometry.coordinates),
t.geometry.type == "MultiPolygon" ?
turf.polygon([t.geometry.coordinates[0][0]]) :
turf.polygon([t.geometry.coordinates[0]])
);
});
//try to update state,and set in-area equals true. so that the condition paint logic will work in the bellow "3d-buildings" layer
//////however,the follow code can't work because the id of feature is undefined
///// 'Error: The feature id parameter must be provided.' will alerted in console
lastInAreaFeatures.forEach(function(f) {
map.setFeatureState(f, {
"in-area": true
});
});
console.log(lastInAreaFeatures);
draw.deleteAll();
}
var lastInAreaFeatures;
// The 'building' layer in the mapbox-streets vector source contains building-height
// data from OpenStreetMap.
map.on("load", function() {
map.addLayer({
id: "3d-buildings",
source: "composite",
"source-layer": "building",
filter: ["==", "extrude", "true"],
type: "fill-extrusion",
minzoom: 15,
paint: {
"fill-extrusion-color": [
"case", ["==", ["feature-state", "in-area"], true],
"green",
"#aaa"
],
"fill-extrusion-height": [
"interpolate", ["linear"],
["zoom"],
15,
0,
15.05, ["get", "height"]
],
"fill-extrusion-base": [
"interpolate", ["linear"],
["zoom"],
15,
0,
15.05, ["get", "min_height"]
],
"fill-extrusion-opacity": 0.6
}
});
});
</script>
</body>
</html>

changing the default layer in leafletjs

In https://leafletjs.com/examples/layers-control/example.html there are two layers - Grayscale and Streets. It defaults to Grayscale but how would one change the default to Streets instead?
In that example there's this:
var map = L.map('map', {
center: [39.73, -104.99],
zoom: 10,
layers: [grayscale, cities]
});
I tried swapping the order of the variables in layers but that didn't do anything.
There's also this:
var baseLayers = {
"Grayscale": grayscale,
"Streets": streets
};
I tried reversing that, as well, without success.
I even tried renaming the names of the layers, thinking that it might be done alphabetically, but no such luck.
Any ideas?
It has to do with which layer has been added to the map. So remove the array layers from the map instance and add streets layer to the map. That will define the preselected layer.
<!DOCTYPE html>
<html>
<head>
<title>Layers Control Tutorial - Leaflet</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" type="image/x-icon" href="docs/images/favicon.ico" />
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.7.1/dist/leaflet.css" integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A==" crossorigin="" />
<script src="https://unpkg.com/leaflet#1.7.1/dist/leaflet.js" integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA==" crossorigin=""></script>
<style>
html,
body {
height: 100%;
margin: 0;
}
#map {
width: 600px;
height: 400px;
}
</style>
</head>
<body>
<div id='map'></div>
<script>
var cities = L.layerGroup();
L.marker([39.61, -105.02]).bindPopup('This is Littleton, CO.').addTo(cities),
L.marker([39.74, -104.99]).bindPopup('This is Denver, CO.').addTo(cities),
L.marker([39.73, -104.8]).bindPopup('This is Aurora, CO.').addTo(cities),
L.marker([39.77, -105.23]).bindPopup('This is Golden, CO.').addTo(cities);
var mbAttr = 'Map data © OpenStreetMap contributors, ' +
'Imagery © Mapbox',
mbUrl = 'https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw';
var map = L.map('map', {
center: [39.73, -104.99],
zoom: 10,
});
var grayscale = L.tileLayer(mbUrl, {
id: 'mapbox/light-v9',
tileSize: 512,
zoomOffset: -1,
attribution: mbAttr
});
var streets = L.tileLayer(mbUrl, {
id: 'mapbox/streets-v11',
tileSize: 512,
zoomOffset: -1,
attribution: mbAttr
}).addTo(map)
var baseLayers = {
"Streets": streets,
"Grayscale": grayscale
};
var overlays = {
"Cities": cities
};
L.control.layers(baseLayers, overlays).addTo(map);
</script>
</body>
</html>

How to add leaflet draw for non-geographical map (e.g. floor plan)?

I try to make an interactive floor plan like this example here but I'm using php. The images are non-geographical. As for now, I can add the floor plan image as the background in the map like this.
But now I'm looking for ways on how to draw polygon, line, etc and edit it (currently it shows error, L.Control.Draw is not a constructor). Here is my code
map-configure.js and mapConfigure.php
$(document).ready(function() {
//get image width and height
var size = document.getElementById("floorplan_size").value.split(',');
//get floorplan image (width X height)
var width = size[0];
var height = size[1];
//floorplan image as background in the map
var imageUrl = "data:image/jpeg;base64," + $("#floorplan_data").val();
var mymap = L.map("mapL", {
crs: L.CRS.Simple,
minZoom: -4
}); //CRS simple referring to normal coordinate value
var bounds = [
[0, 0],
[height, width]
]; // height and width of image is set
mymap.fitBounds(bounds);
var image = L.imageOverlay(imageUrl, bounds).addTo(mymap);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(mymap);
var drawnItems = new L.FeatureGroup(); // FeatureGroup is to store editable layers
mymap.addLayer(drawnItems);
var drawControl = new L.Control.Draw({
draw: {
polygon: {
shapeOptions: {
color: 'purple' //Color for polygon
},
allowIntersection: false,
drawError: {
color: 'orange',
timeout: 1000
},
showArea: true,
metric: true //Can set the measurement units to not be metric (to show acres instead) by setting the metric option to false
},
polyline: {
shapeOptions: {
color: 'red' //Color for polyline
},
},
rect: {
shapeOptions: {
color: 'green' //Color for rectangle
},
},
circle: {
shapeOptions: {
color: 'steelblue' //Color for circle
},
},
},
edit: {
featureGroup: drawnItems
}
});
mymap.addControl(drawControl);
mymap.on('draw:created', function(event) {
var layer = event.layer,
feature = layer.feature = layer.feature || {}; // Intialize layer.feature
feature.type = feature.type || "Feature"; // Intialize feature.type
var props = feature.properties = feature.properties || {}; // Intialize feature.properties
drawnItems.addLayer(layer);
});
}
<!DOCTYPE html>
<html lang="en" data-textdirection="ltr" class="loading">
<head>
<!-- Leaflet and Leaflet Draw -->
<link rel="stylesheet" href="https://unpkg.com/leaflet#1.6.0/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet#1.6.0/dist/leaflet.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
</head>
<body>
<div class="card-block card-dashboard ">
<input type="hidden" id="floorplan_data" name="floorplan_data" value=< ?php echo $_POST[ 'floorplan_data']?> >
<input type="hidden" id="floorplan_size" name="floorplan_size" value= <?php echo $_POST['floorplan_size']?> >
</div>
<div id="mapL"> </div>
<script type="text/javascript" src="leaflet/leaflet.js"></script>
<script type="text/javascript" src="assets/js/map-configure.js"></script>
</body>
</html>
Any tips would be appreciated.

Trying to make a custom marker show up on my exact location when I allow tracking of my location

I want to use the pic userPosition.png on my location when I accept the prompt of asking for location, right now it doesn't work.
<!DOCTYPE html>
<html lang="en">
<head>
<link
rel="stylesheet"
href="https://unpkg.com/leaflet#1.6.0/dist/leaflet.css"
integrity="sha512-xwE/Az9zrjBIphAcBb3F6JVqxf46+CDLwfLMHloNu6KEQCAWi6HcDUbeOfBIptF7tcCzusKFjFw2yuvEpDL9wQ=="
crossorigin=""
/>
<script
src="https://unpkg.com/leaflet#1.6.0/dist/leaflet.js"
integrity="sha512-gZwIG9x3wUXg2hdXF6+rVkLF/0Vi9U8D2Ntg4Ga5I5BZpVkVxlJWbSQtXPSiUTtC0TjtGOmxa1AJPuV0CPthew=="
crossorigin=""
></script>
<script src="./leaflet/leaflet.js"></script>
<style></style>
</head>
<body>
<h2 style="text-align: center;">My interactive map</h2>
<div id="mapid" style="width: 100%;height: 500px;"></div>
<script>
var mymap;
mymap = L.map("mapid").setView([55.70584, 13.19021], 12);
L.tileLayer(
"https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFkc2pvaGFuc2VuIiwiYSI6ImNrNWkxZnA3bzA5NnIza3M2cGczNnprMHcifQ.Z2h9R1lODB6zPZ2Ex92BrA",
{
attribution:
'Map data © OpenStreetMap contributors, CC-BY-SA, Imagery © Mapbox',
maxZoom: 18,
id: "mapbox/streets-v11",
accessToken: "your.mapbox.access.token"
}
).addTo(mymap);
function onLocationFound(e) {
var radius = e.accuracy / 2;
L.marker(e.latlng)
.addTo(map)
.bindPopup("You are within " + radius + " meters from this point")
.openPopup();
}
function onLocationError(e) {
alert(e.message);
}
map.on("locationfound", onLocationFound);
map.on("locationerror", onLocationError);
map.locate({
setView: true,
maxZoom: 16
});
var popup = L.popup();
var marker = L.marker();
var circle = L.circle();
var newMarkerIcon = L.Icon.extend({
options: {
iconSize: [38, 95],
shadowSize: [50, 64],
iconAnchor: [22, 94],
shadowAnchor: [4, 62],
popupAnchor: [-3, -76]
}
});
var blackMarker = new newMarkerIcon({ iconUrl: "userPosition.png" });
function onMapClick(e) {
L.marker(e.latlng, "Insert postition PNG pic here")
.addTo(mymap)
.bindPopup("You clicked the map at " + e.latlng.toString());
}
mymap.on("click", onMapClick);
</script>
</body>
</html>
Use this :
L.marker([e.latlng], { icon: YourIconHere })
.bindPopup("You are within " + radius + " meters from this point")
.openPopup()
.addTo(map);
You can also add circle like that:
L.circle([e.latlng], radius,
{ weight: 1, color: 'blue', fillColor: '#cacaca', fillOpacity: 0.2 })
.addTo(map);

Leaflet.Deflate with Leaflet.markercluster does not show cluster coverage on hover

when you mouse over a cluster, Leaflet.markercluster should show the bounds of its markers. this is the (simplified) code I am using:
map = new L.Map('map');
L.tileLayer(
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 13,
attribution: 'Map data © OpenStreetMap contributors'
}
).addTo(map);
map.setView([51.505, -0.09], 11);
let deflate_features = L.deflate({
minSize: 40,
markerCluster: true
});
deflate_features.addTo(map);
var polygon = L.polygon([
[51.509, -0.08],
[51.503, -0.06],
[51.51, -0.047]
]);
deflate_features.addLayer(polygon);
var polyline = L.polyline([
[51.52, -0.05],
[51.53, -0.10],
], {
color: 'red'
});
deflate_features.addLayer(polyline);
#map {position: absolute; top: 0; bottom: 0; left: 0; right: 0;}
<html>
<head>
<link href="https://unpkg.com/leaflet#1.3.3/dist/leaflet.css" rel="stylesheet" />
<link href="https://unpkg.com/leaflet.markercluster#1.3.0/dist/MarkerCluster.css" rel="stylesheet" />
<link href="https://unpkg.com/leaflet.markercluster#1.3.0/dist/MarkerCluster.Default.css" rel="stylesheet" />
<script src="https://unpkg.com/leaflet#1.3.3/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet.markercluster#1.3.0/dist/leaflet.markercluster.js"></script>
<script src="https://unpkg.com/Leaflet.Deflate#1.0.0-alpha.2/dist/L.Deflate.js"></script>
</head>
<body>
<div id="map"></div>
</body>
</html>
why does the cluster coverage on hover not show?
coverage is not shown if there are only 2 objects... :|
adding a third object, e.g.:
var polyline2 = L.polyline([
[51.535, -0.1],
[51.525, -0.05],
], {
color: 'green'
});
deflate_features.addLayer(polyline2);
enables cluster coverage: