I'm new to mapbox GL JS and am following this example:
Add custom markers in Mapbox GL JS
https://www.mapbox.com/help/custom-markers-gl-js/
Let's say I modify the example above to include 100 different animal markers. How do I change the draggable property of a specific marker after it has been added to the map?
Example: Change the draggable property of the dog marker.
It would be nice to do something like this:
map.getMarker('dog').setDraggable(true);
I don't see a way to query any of the markers added to my map or modify a specific marker's properties like setLatLng, setDraggable after they have been added to a map. There is no method to get the collection of markers added to a map. Thanks for any help!
For change marker property like draggable check its api. IE https://www.mapbox.com/mapbox-gl-js/api/#marker#setdraggable
Mapbox custom marker is build by html element. If you want to change visual display of custom marker element, you should update Its inside html. For example, here are 2 functions I use to create a div with image background then return it as a image marker
/**
* #function CustomMarkerWithIcon(): constructor for CustomMarker with image icon specify
* #param lnglat: (LngLat) position of the marker
* map: (Map) map used on
* icon: (image) object for custom image
*/
function CustomMarkerWithIcon(lnglat, map, icon) {
var el = document.createElement('div');
el.className = 'marker';
el.style.backgroundImage = 'url("' + icon.url + '")';
el.style.backgroundSize = 'cover';
el.style.width = icon.size.width;
el.style.height = icon.size.height;
el.style.zIndex = icon.zIndex;
return new mapboxgl.Marker(el)
.setLngLat(lnglat)
.addTo(map);
}
/**
* #function ChangeMarkerIcon(): change marker icon
* #param marker: (marker) marker
* icon: (image) object for custom image
*/
function ChangeMarkerIcon(marker, icon) {
var el = marker.getElement();
el.style.backgroundImage = 'url("' + icon.url + '")';
}
You're right: Mapbox GL JS doesn't store references to markers. However, you can store your own references to the markers in an array at the time that you generate them.
In this example below, I am looping over a set of GeoJSON point features and creating a custom HTML marker for each:
let markersArray = [];
geojson.features.forEach(feature => {
// create a HTML element for each feature
let el = document.createElement("div");
el.className = "marker";
el.innerHTML = `
<img src="custom-marker.png" height="20px" width="20px" />
<span class="marker-label">${feature.properties.name}</span>
`;
// make a marker for each feature and add to the map
let marker = new mapboxgl.Marker({
element: el,
anchor: "top"
})
.setLngLat(feature.geometry.coordinates)
.addTo(map);
// add to my own array in an object with a custom 'id' string from my geojson
markersArray.push({
id: feature.properties.id,
marker
});
});
This id string can be whatever you want. You can even store other parameters if you want to be able to query other things, like latitude/longitude:
markersArray.push({
id: feature.properties.id,
coordinates: feature.geometry.coordinates,
marker
});
Then, if I want to access the marker's instance members (like setDraggable), I can use Array.find() to return the first instance that matches my search parameters in markersArray:
let someIdQuery = "some-id";
let queriedMarkerObj = markersArray.find(
marker => marker.id === someIdQuery
);
queriedMarkerObj.marker.setDraggable(true);
(Note that Array.find() just returns the first instance in the array that matches your condition. Use something like Array.filter() if you want to be able to query for more than one marker.)
Related
I need to draw a rectangle on a map with Leaflet, and remove it when I create another one.
I call a function to do that and zoom on the rectangle.
It creates the rectangle, but I'm unable to remove it when calling the function again.
I can remove it if I do that just after having created the rectangle.
What's wrong with it ?
I'm a Leaflet and Javascript beginner...
Thanks,
function showLoc(lat,lng,zoom) {
map.setView([lat,lng],zoom);
locSquare.remove(map)
//if (map.hasLayer(locSquare)) {locSquare.remove(map)};
var locSquare = L.rectangle([[lat + 0.0208333333, lng - 0.0416666666],[lat - 0.0208333333, lng + 0.0416666666]], {color: "red", weight: 2,fillColor:"red"}).addTo(map);
//if (map.hasLayer(locSquare)) {locSquare.remove(map)};
}
I have made this code from yours to produce the desired output, see below.
You will need to replace the default class name I have put in classRectangle to the one you want.
In the code you wrote there was a problem with the variable locSquare at the line 3. This variable is not defined before you try to remove it from the map because you define it two lines below with the var locSquare.
You should have put the initialization of the variable outside the function for it to work.
This is what we call the scope of a variable. If you initialize it as you do inside the function, once the function is finished you will not be able to use this variable.
Instead of using a variable, you can choose to use an option available for the Rectangle object in Leaflet : className. With this option you can specify to Leaflet that this Rectangle has to use the CSS class you provided. In this way if you want to remove the Rectangle from the map you can just specify which class you want to target.
/**
* #param {number} lat
* #param {number} lng
* #param {number} zoom
*/
function showLoc(lat,lng,zoom) {
// Here is the class we will use to name the Rectangle we want to display on the map and eventually delete
const classRectangle = 'your_class_here';
// We retrieve every Rectangle on the map with the class specified before. We should only have zero or one class
classes = document.getElementsByClassName(classRectangle);
if(classes.length > 0) // If there is more than zero element that means we already have a rectangle on the map
{
classes[0].remove(); // we remove the existing Rectangle from the DOM and the map
}
let locSquare = L.rectangle(
[
[lat + 0.0208333333, lng - 0.0416666666],
[lat - 0.0208333333, lng + 0.0416666666]
],
{
color: "red",
weight: 2,
fillColor: "red",
className: classRectangle // the option className is used to add a class to the DOM element that will be created
}
).addTo(map);
map.setView([lat, lng], zoom); // We set the view depending on the params call with the function
}
Just a quick question, if anyone has ever replaced the Mapbox default marker with a Maki icon. I've only seen examples of using Maki icons for point tilesets/layers, but I'm wanting to use it for non-tileset features, specifically replacing the marker that adds after geocoding, at the location of the address just geocoded.
Or, trying to find something that is similar to Google Maps symbols below. Any suggestions appreciated.
var pinImage = {
path: google.maps.SymbolPath.CIRCLE,
fillColor: '#ff4b00',
fillOpacity: .9,
scale: 5,
strokeColor: '#CDDC39',
strokeWeight: 0,
strokeOpacity: .5
}
The MapboxGeocoder control has a marker option https://github.com/mapbox/mapbox-gl-geocoder/blob/master/API.md#parameters which controls the marker placed on the map when you select a result.
There is an example at https://docs.mapbox.com/mapbox-gl-js/example/custom-marker-icons/ to create a Marker with a custom icon.
So you could create an HTML Element which contains either an SVG or PNG icon from Maki and use that as your element in your custom Marker passed to the MapboxGeocoder control.
I think this sample is what you are looking for Use a custom render function with a geocoder
It allows you to add a custom render function including the icon...
var geocoder = new MapboxGeocoder({
accessToken: mapboxgl.accessToken,
types: 'poi',
// see https://docs.mapbox.com/api/search/#geocoding-response-object for information about the schema of each response feature
render: function(item) {
// extract the item's maki icon or use a default
var maki = item.properties.maki || 'marker';
return (
"<div class='geocoder-dropdown-item'><img class='geocoder-dropdown-icon' src='https://unpkg.com/#mapbox/maki#6.1.0/icons/" +
maki +
"-15.svg'><span class='geocoder-dropdown-text'>" +
item.text +
'</span></div>'
);
},
mapboxgl: mapboxgl
});
map.addControl(geocoder);
I have a function code that pulls data from the "description" field and displays it inside a popup on mouseenter, but can someone help me figure out how to pull in the URL stored inside "linkurl" and use it to open that URL when the icon is clicked? The popup displays properly over an icon, but I can't figure out how to bring the URL in as a link on click. Here's the code I'm working with:
map.on('load', function() {
// Create a popup, but don't add it to the map yet.
var popup = new mapboxgl.Popup({
closeButton: false,
closeOnClick: false
});
// POINTS OF INTEREST
function showPopup(e) {
// Updates the cursor to a hand (interactivity)
map.getCanvas().style.cursor = 'pointer';
// Show the popup at the coordinates with some data
popup
.setLngLat(e.features[0].geometry.coordinates)
.setHTML(checkEmpty(e.features[0].properties.description))
.addTo(map);
}
function hidePopup() {
map.getCanvas().style.cursor = '';
popup.remove();
}
function checkEmpty(info) {
return (info) ? info : "No data";
}
// CHANGE: Add layer names that need to be interactive
map.on('mouseenter', 'points-of-interest-2019', showPopup);
map.on('mouseleave', 'points-of-interest-2019', hidePopup);
});
To add a link within the Popup itself, you can use Popup#setHTML in conjunction with the <a> tag define a hyperlink. For example:
// Show the popup at the coordinates with some data
var properties = e.features[0].properties;
popup
.setLngLat(e.features[0].geometry.coordinates)
.setHTML(
'<a href=\'' + properties.linkurl + '\'>'
+ checkEmpty(properties.description)
+ '</a>')
.addTo(map);
Since creating a Popup with the GL JS API automatically creates a DOM element as outlined in the source code here, there is currently not a way to make the entire Popup clickable to navigate to a particular link. You could instead use Popup#setHTML along with some minimal CSS to create a link which wraps the entire content added to the Popup, so that clicking the content of the Popup will open the link.
Alternatively, if you are using Marker instances and would like clicking on the marker itself to open a link, you could utilize the options.element parameter to specify a DOM element wrapped in a link to use as a marker. For example, consider a slight modification to this example:
var el = document.createElement('a');
el.href = 'https://www.mapbox.com/'
el.className = 'marker';
el.style.backgroundImage =
'url(https://placekitten.com/g/' +
marker.properties.iconSize.join('/') +
'/)';
el.style.width = marker.properties.iconSize[0] + 'px';
el.style.height = marker.properties.iconSize[1] + 'px';
// add marker to map
new mapboxgl.Marker(el)
.setLngLat(marker.geometry.coordinates)
.addTo(map);
I have displayed the the result markers for the leaflet-knn on the map with following code:
const myloc = new L.LatLng(13.7433242, 100.5421583);
var gjLayer = L.geoJson(testCities, {
onEachFeature: function(feature, layer) {
content = "<b>Name:</b> " + feature.properties.name;
layer.bindPopup(content);
}
});
var longitude = myloc.lng,
latitude = myloc.lat;
var res = leafletKnn(gjLayer).nearest(
[longitude, latitude], 5, distance);
for (i = 0; i < res.length; i++) {
map.addLayer(res[i].layer);
}
Now I want to change the color of this marker that is added or I want to change the icon.
Can anybody tell me how can I do?
Leaflet-knn is agnostic when it comes to the representation of the results - it relies on the existing L.Layers: it takes a L.GeoJSON as an input and then iterates through all its members in order to fetch all their coordinates (in the case of polylines and polygons) and then store a reference to the L.Layer for each of its coordinates.
The results of a leaflet-knn search include the original L.Layer from the L.GeoJSON that was passed at instantiation time.
Either symbolize your GeoJSON afterwards, as explained in the Leaflet tutorials, or create new markers/symbols for the results after each query.
Right now your code is relying on the default symbolization of GeoJSON data (instantiate a L.Marker with a L.Icon.Default for points). I suggest the approach of displaying your L.GeoJSON in the map to ensure that it looks like you want it to (even if it's a partial set of the data), then implementing the leaflet-knn search.
How to add a custom icon in mapbox?
var map = L.mapbox.map('map', 'mapbox.streets').setView([0, 0], 1);
var geojson = { type: 'LineString', coordinates: value};
var start = value[0];
var end = value[value.length-1];
geojson.coordinates.push((start,end).slice());
// Add this generated geojson object to the map.
L.geoJson(geojson).addTo(map);
// Create a marker and add it to the map.
var marker = L.marker(end, {
icon: L.mapbox.marker.icon({
"iconUrl": "https://www.mapbox.com/mapbox.js/assets/images/astronaut2.png"
})
}).addTo(map);
});
I can't able to add a custom icon in above code. Please help me..
Thanks.
First you will have to create a var, for example 'myIcon', then simply replace the iconUrl with a path that specifies the custom marker you want to use.
You can use the iconSize option to specify the size of your marker
You can use the iconAnchor option to specify which part of your masker should be placed on the latlng.
myIcon=L.icon({
iconUrl:'img/custom-marker.png',
iconSize: [25,30]
});
Then create the marker, set the lat lng where you want your marker to be placed. And specify the icon you want to use.
var Marker = new L.Marker ( latlng, {icon:myIcon});
Finally add your market to the map:
map.addlayer(Marker);