refresh leaflet map: map container is already initialized - javascript

I have a page where given a select to the user he can switch the leaflet map I show.
After a initial leaflet map load, my problem is when i want to refresh the map.
I always get "Map container is already initialized":
The problem line is:
var map = L.map('mapa').setView([lat, lon], 15);
Initially it loads well, but when I select another parameter in the form and want to display the map another time it crashes.
btw, I've tried to destroy and recreate $('#mapa') with jQuery before the second setView() but it shows the same error.

Try map.remove(); before you try to reload the map. This removes the previous map element using Leaflet's library (instead of jquery's).

the best way
map.off();
map.remove();
You should add map.off(), it also works faster, and does not cause problems with the events

Html:
<div id="weathermap"></div>
JavaScript:
function buildMap(lat,lon) {
document.getElementById('weathermap').innerHTML = "<div id='map' style='width: 100%; height: 100%;'></div>";
var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttribution = 'Map data © OpenStreetMap contributors,' +
' CC-BY-SA',
osmLayer = new L.TileLayer(osmUrl, {maxZoom: 18, attribution: osmAttribution});
var map = new L.Map('map');
map.setView(new L.LatLng(lat,lon), 9 );
map.addLayer(osmLayer);
var validatorsLayer = new OsmJs.Weather.LeafletLayer({lang: 'en'});
map.addLayer(validatorsLayer);
}
I use this:
document.getElementById('weathermap').innerHTML = "<div id='map' style='width: 100%; height: 100%;'></div>";
to reload content of div where render map.

Before initializing map check for is the map is already initiated or not
var container = L.DomUtil.get('map');
if(container != null){
container._leaflet_id = null;
}

Only use this
map.invalidateSize();
https://github.com/Leaflet/Leaflet/issues/690

well, after much seeking i realized it's well documented at http://leafletjs.com/examples/layers-control.html
i've ended not repainting the map, but print it once and repaint the points on each new ajax call, so the problem was how to clean up the old points and print only the new ones. i've ended doing this:
var point = L.marker([new_marker[0], new_marker[1]]).addTo(map).bindPopup('blah blah');
points.push(point);
//points is a temporary array where i store the points for removing them afterwards
so, at each new ajax call, before painting the new points, i do the following:
for (i=0;i<points.length;i++) {
map.removeLayer(points[i]);
}
points=[];
so far, so good :-)

When you just remove a map, it destroys the div id reference, so, after remove() you need to build again the div where the map will be displayed, in order to avoid the "Uncaught Error: Map container not found".
if(map != undefined || map != null){
map.remove();
$("#map").html("");
$("#preMap").empty();
$( "<div id=\"map\" style=\"height: 500px;\"></div>" ).appendTo("#preMap");
}

What you can try is to remove the map before initialising it or when you leave the page:
if(this.map) {
this.map.remove();
}

I had the same problem on angular when switching page. I had to add this code before leaving the page to make it works:
$scope.$on('$locationChangeStart', function( event ) {
if(map != undefined)
{
map.remove();
map = undefined
document.getElementById('mapLayer').innerHTML = "";
}
});
Without document.getElementById('mapLayer').innerHTML = "" the map was not displayed on the next page.

if you want update map view, for example change map center, you don’t have to delete and then recreate the map, you can just update coordinate
const mapInit = () => {
let map.current = w.L.map('map');
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map.current);
}
const setCoordinate = (gps_lat, gps_long) => {
map.setView([gps_lat, gps_long], 13);
}
initMap();
setCoordinate(50.403723 30.623538);
setTimeout(() => {
setCoordinate(51.505, -0.09);
}, 3000);

You should try to unmount the function in react js to remove the existing map.
const Map = () => {
const mapContainer = useRef();
const [map, setMap] = useState({});
useEffect(()=>{
const map = L.map(mapContainer.current, {attributionControl: false}).setView([51.505, -0.09], 13);
L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
maxZoom: 18,
attribution: 'Map',
id: 'mapbox/streets-v11',
tileSize: 512,
zoomOffset: -1
}).addTo(map);
// unmount map function
return () => map.remove();
}, []);
return (
<div style={{padding: 0, margin: 0, width: "100%", height: "100vh",}}
ref={el => mapContainer.current = el}>
</div>
);
}

I had same problem.then i set globally map variable e.g var map= null and then for display map i check
if(map==null)then map=new L.Map('idopenstreet').setView();
By this solution your map will be initialize only first time after that map will be fill by L.Map then it will not be null. so no error will be there like map container already initialize.

For refreshing map in same page you can use below code to create a map on the page
if (!map) {
this.map = new L.map("mapDiv", {
center: [24.7136, 46.6753],
zoom: 5,
renderer: L.canvas(),
attributionControl: true,
});
}
then use below line to refresh the map, but make sure to use same latitude, longitude and zoom options
map.setView([24.7136, 46.6753], 5);
Also, I had the same issue when switching between tabs in the same page using angular 2+, and I was able to fix it by adding below code in Component constructor
var container = L.DomUtil.get('mapDiv');
if (container != null) {
container.outerHTML = ""; // Clear map generated HTML
// container._leaflet_id = null; << didn't work for me
}

use the redrawAll() function rather than renderAll().

We facing this issue today and we solved it. what we do ?
leaflet map load div is below.
<div id="map_container">
<div id="listing_map" class="right_listing"></div>
</div>
When form input change or submit we follow this step below. after leaflet map container removed in my page and create new again.
$( '#map_container' ).html( ' ' ).append( '<div id="listing_map" class="right_listing"></div>' );
After this code my leaflet map is working fine with form filter to reload again.
Thank you.

If you don't globally store your map object reference, I recommend
if (L.DomUtil.get('map-canvas') !== undefined) {
L.DomUtil.get('map-canvas')._leaflet_id = null;
}
where <div id="map-canvas"></div> is the object the map has been drawn into.
This way you avoid recreating the html element, which would happen, were you to remove() it.

For refresh leaflet map you can use this code:
this.map.fitBounds(this.map.getBounds());

I had the same problem on react I solved it by initialized at the top in useEffect
Here is my React Code.
const mapContainerRef = useRef(null);
useEffect( async () => {
const res =await Axios.get(BASE_PATH + 'fetchProperty')
const container = L.DomUtil.get(mapContainerRef.current); if(container != null){ container._leaflet_id = null; }
if(container) {
const mapView = L.map( mapContainerRef.current, {
zoom: 13,
center: [19.059984, 72.889999]
// maxZoom: 13
// minZoom: 15
});
// const canvas = mapView.getCanvasContainer();
mapView.zoomControl.setPosition("bottomright");
mapView.attributionControl.addAttribution(
"<a href='https://mascots.pro'>Mascots. pro</a>"
);
L.tileLayer(
// "https://api.mapbox.com/styles/v1/mapbox/dark-v9/tiles/{z}/{x}/{y}?access_token=" + https://api.mapbox.com/styles/v1/anonymousmw/cko1eb1r20mdu18qqtps8i03p/tiles/{z}/{x}/{y}?access_token=
"https://api.mapbox.com/styles/v1/mapbox/dark-v9/tiles/{z}/{x}/{y}?access_token=" +
access_token,
{
attribution: 'Mascots'
}
).addTo(mapView);
const mask = L.tileLayer.mask(
"https://api.mapbox.com/styles/v1/anonymousmw/cko1eb1r20mdu18qqtps8i03p/tiles/{z}/{x}/{y}?access_token=" +
access_token,
{
attribution: 'Mascots pro',
maskSize: 300
// maxZoom: 18,
// maxNativeZoom: 16
// tms: true
}
)
.addTo(mapView);
mapView.on("mousemove", function (e) {
mask.setCenter(e.containerPoint);
});
res.data.map((marker) => {
const innerHtmlContent = `<div id='popup-container' class='popup-container'> <h3> Property Details</h3>
<div class='popup-label'>Building Name :<p>${marker.Building}</p></div>
<div class='popup-address-label'> Address : <p>${marker.Landmark}, ${marker.Location}</p></div>
<div class='popup-rent-label'>Monthly Rent : <p> ₹ ${marker.Price}</p></div>
</div>`;
const divElement = document.createElement("div");
const assignBtn = document.createElement("div");
assignBtn.className = "map-link";
assignBtn.innerHTML = `<button class="view-btn">View Property</button>`;
divElement.innerHTML = innerHtmlContent;
divElement.appendChild(assignBtn);
assignBtn.addEventListener("click", (e) => {
console.log("dsvsdvb");
});
var iconOptions = {
iconUrl: "/images/location_pin2.svg",
iconSize: [25, 25]
};
var customIcon = L.icon(iconOptions);
// create popup contents
var customPopup = divElement;
// specify popup options
var customOptions = {
maxWidth: "500",
className: "custom"
};
const markerOptions = {
// title: "MyLocation",
// draggable: true
clickable: true,
icon: customIcon
};
const mark = L.marker([marker.Latitude,marker.Longitude], markerOptions);
mark.bindPopup(customPopup, customOptions);
mark.addTo(mapView);
// return mapView.off();
});
return () => mapView.remove();
}
}, [])
return (
<div className="map-box">
<div className="map-container" ref={mapContainerRef}></div>
</div>
);

I went through the same problem, so I created a method inside the map instance to reload it.
var map = L.map('mapa').setView([lat, lon], 15);
map.reload = function(){
map.remove();
map = L.map('mapa').setView([lat, lon], 15);
}
....
map.reload();

I did this in reactjs
// Create map (dev = reuse existing map)
let myMap = L.DomUtil.get('map');
if(myMap == null){
myMap = L.map('mapid').setView(currentLocation, zoom);
}

Html:
<div id='leaflet-map' #leafletMap></div>
JavaScript:
#ViewChild('leafletMap')
private mapElement: ElementRef;
private initMap(): void {
this.map = leaflet.map(this.mapElement.nativeElement, {
center: [39.01860177826393, 35.30274319309024],
zoom: 4,
});
leaflet
.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '',
maxZoom: 18,
})
.addTo(this.map);
}

My hacky implementation to refresh the map was to use:
// Hack to refresh the map by panning by zero
public refreshMap() {
this.map.panBy([0,0]);
}

In case you're working with NextJs and typescrypt, what worked for me was
container._leaflet_id = null;
as someone proposed, but had some typing erros so my approach is
const L = await import('leaflet');
const container = L.DomUtil.get('map');
if (!container) return;
if (container.classList.contains('leaflet-container')) return;
const map = L.map('map', {
center: [19.434817, -99.1268643],
zoom: 18,
});
map.invalidateSize();
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
}).addTo(map);
const marker = L.marker([19.434786638353515, -99.1268643025101]).addTo(
map
);
after this code, (all these code needs to be inside a useEffect hook), leaflet just worked fine.

set
var container = L.DomUtil.get('map');
if (container && container['_leaflet_id'] != null) {
container.remove();
}
before var map = L.map('map')
enjoy :)

For web apps, check these things
I've experienced the error "Map container is already initialized" in multiple contexts (e.g. showing/hiding a map, changing map components or triggering hot-reload during development) and ultimately I had to implement multiple solutions to truly clear this error. In general you want to be conservative.
(A) Use a unique ID for the map container
Even if you have only one map instance, use a dynamic unique id anyways. This way you're ensured that every time the component mounts, the DOM element truly is fresh.
For example, in Vue I do this:
<div :id="id"></div>
data() {
return {
id: 'mapLeaflet-'+Date.now()
}
}
And anywhere I refer to the DOM element, I use this.id:
let mapHTMLContainer = document.getElementById(this.id)
(B) Before map init, check existence of map first
Check for DOM element and whether you already have a map instance set in data.
In Vue, I do this:
methods: {
initMap() {
let mapHTMLContainer = document.getElementById(this.id)
if (mapHTMLContainer
&& mapHTMLContainer.hasChildNodes() == false
&& !this.map) {
// DO STUFF
// e.g. initialize leaflet, set tile layer, add markers, etc
}
}
}
(C) On map init, pass the actual HTML element, not just a string
When initiating Leaflet map, pass the actual HTML element instead of just the string id.
Both methods are officially supported, but I cannot find any information talking about the differences. https://leafletjs.com/reference.html#map-l-map
BEFORE
This works in general and is convenient...
this.map = L.map("mymap")
AFTER
... but, instead try to actually get the element, and pass it. I observed a difference doing this.
let myMapElement = document.getElementById(this.id)
this.map = L.map(myMapElement)
(D) On component "dismount", destroy things
If you have a dismount state, use it and destroy stuff related to your map component. Make sure things don't hang-around.
In Vue, I do this:
beforeDestroy() {
// Destroy any listeners
this.$nuxt.$off('refreshMap') // Nuxt is the framework I use
// Clear the map instance in your component
if (this.map) {
// I haven't fully tested these; are they valid?
this.map.off() // Leaflet function
this.map.remove() // Leaflet function
this.map = null // null it for good measure
}
// Clear out the HTML container of any children, just in case
let mapHTMLElement = document.getElementById(this.id)
if (mapHTMLElement) {
mapHTMLElement.outerHTML = ""
}
}

Related

How to use FitBounds in deckgl on timer without npm and es6

I've a deckGL map in a div-container.
let deckMap = new deck.DeckGL({
mapStyle: 'https://basemaps.cartocdn.com/gl/positron-nolabels-gl-style/style.json',
initialViewState: {..
},
layers: [geoJSONLayer],
getTooltip,
controller: true,
onViewStateChange: ({ viewState }) => {
console.log("View Change");
deckMap.setProps({ viewState })
}
});
That thing "overlays"(?) my normal mapgl map
const map = new mapboxgl.Map({
container: 'map',
interactive: false,
style: carto.basemaps.voyager,
center: [INITIAL_VIEW_STATE.longitude, INITIAL_VIEW_STATE.latitude],
zoom: INITIAL_VIEW_STATE.zoom
});
I'm running a cyclic download of a GeoJSON file and want to display the data. that already works. I also can calculate the bounds using bounds.extend.
But I can't find a way to set the zoom/fit the bounds.
What I've tried
map.fitBounds is just not working. It does nothing
deckMap.fitBound => unknown command
WebMercatorViewport => needs an import, import is unknown, including it requires app.js to be a module which screws the whole code.
viewport=info.context with that I could set viewport.fitBounds. Problem: What is info? I found this example in an event driven approach and info is the layer that the user interacts with. But simply using my geoJSONLayer does not work; fitBounds is not existent
Calculate zoom by hand. Can't find the algorithm anymore, but it was for GoogleEarth and I think it was broken in general.
How do I get the damned fitBounds working or - alternatively calculate the zoom for deckGL? I wasted 5 hours on that today!
There is a working example of deck.gl fitBounds on map initialization here. To run:
Clone this repository;
Navigate into the repository and run npm i && npm start;
Go to http://localhost:8080/ and click the Toggle deck.gl mode button at the top. This will load a deck.gl instance and fit the map bounds to the data.
This gist helped me get things working:
https://gist.github.com/tomsoderlund/a2040d659aafe4064e4060f561aca6d1
Steps:
In case someone else will find this useful, the fit bounds process can be started with an event. In the case of the question, when a new geoJSON has been successfully fetched. This could also be triggered when a user clicks an auto-fit map to visible data button, or in the example above, on deck.gl initialization, etc...
Next, a helper function is used to calculate opposite corners of your bounds by passing an array of point type coordinates of data visible on your map:
fitBounds = (coords) => {
let latMin = 90;
let latMax = -90;
let lonMin = 180;
let lonMax = -180;
coords.forEach(function (coord) {
const RECT_LAT_INDEX = 'lat';
const RECT_LON_INDEX = 'lon';
if (coord[RECT_LAT_INDEX] < latMin) latMin = coord[RECT_LAT_INDEX];
if (coord[RECT_LAT_INDEX] > latMax) latMax = coord[RECT_LAT_INDEX];
if (coord[RECT_LON_INDEX] < lonMin) lonMin = coord[RECT_LON_INDEX];
if (coord[RECT_LON_INDEX] > lonMax) lonMax = coord[RECT_LON_INDEX];
});
const bounds = [
[lonMin, latMax],
[lonMax, latMin],
];
return bounds;
}
Create an instance of WebMercatorViewport and pass the same height and width parameters as your map;
const view = new WebMercatorViewport({ width: 800, height: 600 });
Call the fitBounds function on this WebMercatorViewport instance. The response will contain latitude, longitude and zoom attributes so I've destructured them below:
const { latitude, longitude, zoom } = view.fitBounds(bounds)
Set the latitude, longitude, zoom attriubtes on your map view state:
const INITIAL_VIEW_STATE = {
latitude,
longitude,
zoom,
maxZoom: 20,
pitch: 45,
bearing: 0,
};
The map can then be rendered with something like:
return (
<DeckGL
layers={layers}
initialViewState={INITIAL_VIEW_STATE}
controller={true}
>
<div style={COPYRIGHT_LICENSE_STYLE}>
{"© "}
<a
style={LINK_STYLE}
href="http://www.openstreetmap.org/copyright"
target="blank"
>
OpenStreetMap contributors
</a>
</div>
</DeckGL>
);
Padding can be added like below so that it fits nicely onto your map, something like:
const { latitude, longitude, zoom } = new WebMercatorViewport({ width: 800, height: 600 }).fitBounds(bounds, { padding: { top: 100, bottom: 100, left: 100, right: 100 } })

Find my location with Leaflet [duplicate]

I'm trying to locate a user and set the map to this position with leaflet:
<script>
var map;
function initMap(){
map = new L.Map('map',{zoomControl : false});
var osmUrl = 'http://{s}.tile.openstreetmap.org/mapnik_tiles/{z}/{x}/{y}.png',
osmAttribution = 'Map data © 2012 OpenStreetMap contributors',
osm = new L.TileLayer(osmUrl, {maxZoom: 18, attribution: osmAttribution});
map.setView(new L.LatLng(51.930156,7.189230), 7).addLayer(osm);
}
function locateUser(){
map.locate({setView : true});
}
</script>
On execute the browser ask for permission, but then nothing happens? What's wrong with my code?
Here is a quick hack. I recommend this plugin https://github.com/domoritz/leaflet-locatecontrol
var loadMap = function (id) {
var HELSINKI = [60.1708, 24.9375];
var map = L.map(id);
var tile_url = 'http://{s}.tile.osm.org/{z}/{x}/{y}.png';
var layer = L.tileLayer(tile_url, {
attribution: 'OSM'
});
map.addLayer(layer);
map.setView(HELSINKI, 19);
map.locate({setView: true, watch: true}) /* This will return map so you can do chaining */
.on('locationfound', function(e){
var marker = L.marker([e.latitude, e.longitude]).bindPopup('Your are here :)');
var circle = L.circle([e.latitude, e.longitude], e.accuracy/2, {
weight: 1,
color: 'blue',
fillColor: '#cacaca',
fillOpacity: 0.2
});
map.addLayer(marker);
map.addLayer(circle);
})
.on('locationerror', function(e){
console.log(e);
alert("Location access denied.");
});
};
loadMap('map');
You have an issue with the scope of your map variable. I have posted an example that fixes your code here: http://jsfiddle.net/XwbsU/3/
You should receive the browser geolocation popup when you click 'Find me!'.
Hopefully that helps you.
alternatively you can put all your code under getLocation() function and call that function at the loading of the webpage and you should be all set. I used this way and as soon as browser loads it popups a question to share data.

Mapbox marker click event in Ionic 2 / Angular 2

I'm getting started with Ionic 2 / Angular 2 and trying to implement Mapbox into my app.
To display custom markers (code example here) Mapbox expects a DOM element, which, as far as I understand it, is not really the Angular way. I want to add a click event on the marker but because Mapbox uses elements, I'm not entirely sure how to approach this cleanly "the Angular way".
Basically, this is the latest version (showing the map and the marker works, but predictably when I click the marker the event listener can't find this.onMarkerClicked):
export class HomePage {
//(...)
refreshMapPosition() {
/*Initializing Map*/
mapboxgl.accessToken = this.config.mapbox_public_token;
this.map = new mapboxgl.Map({ /*https://www.mapbox.com/mapbox-gl-js/api/#map*/
style: 'mapbox://styles/mapbox/light-v9',
center: [this.Coordinates.longitude, this.Coordinates.latitude],
zoom: 16,
pitch: 80,
minZoom: 7.5,
maxZoom: 17,
container: 'map',
interactive: false,
});
var elCreature = document.createElement('div');
elCreature.className = 'icon-creature alpaca';
elCreature.addEventListener('click', function() {
this.onMarkerClicked();
});
var markerCreature = new mapboxgl.Marker(elCreature, {offset: [-20, -20]})
.setLngLat([this.Coordinates.longitude, this.Coordinates.latitude])
.addTo(this.map);
}
onMarkerClicked() {
console.log("click");
}
}
I'd be much happier if it was possible to have elCreature coming from a component, where I could use <div class="icon-creature alpaca" (click)="onMarkerClicked"></div>. What's the best approach there?
// on your marker HTML
var _self = this;
var _data = this.value;
el.addEventListener('click', function() {
self.markerClicked(_data);
});
// angular method
markerClicked(value){
console.log(value);
}
So I found something that maybe could help you, I created a marker as the documentation mentioned and then I got the element of that market with the getElement() function, after that I added the event to the marker, I do not know if it works with various markers but you can try.
var marker = new mapboxgl.Marker()
.setLngLat([lng, lat])
.addTo(this.map);
marker.getElement().addEventListener("click", function(){
console.log("function");
});
After sleep a while I found that there another thing that you can use, so you can create a Popup first and then add that Popou to a Marker object with the setPopup() method and actually it like an onClick event, because, when you click the Marker the Popup appears. Here is an example.
var popup = new mapboxgl.Popup()
.setLngLat(coordinates)
.setHTML("<h1>Hello</h1>");
var marker = new mapboxgl.Marker()
.setLngLat(coordinates)
.setPopup(popup)
.addTo(this.map);
The variable "coordinates" is an array, such that coordinates = [lng,lat], and this.map is just a variable under my Angular class that call to the mapboxgl.Map object.
You have to know that I am using Ionic 4 in this case. If you need more information please tell me.
Regards.

Leaflet popups for specific base maps

so I'm making a website using leaflet with dozens of base maps. I want to incorporate information about each map that is only visible if the user wants it. To do this, I would like to make an overlay map with popups, but I want the popups to change depending on the base map selected by the user.
How would I go about doing this?
Thank You So Much
You need to either use a plugin that keeps track of the base maps for you (like active layers) or you need to do it yourself.
If you are using the Leaflet layers control, you can subscribe to the basemapchange event to do this easily.
You need two things: active base layer management (easy) and dynamic popups (not too hard)
To wit:
First, here is the event handler to track active base layer when it changes.
map.on("baselayerchange",
function(e) {
// e.name has the layer name
// e.layer has the layer reference
map.activeBaseLayer = e.layer;
console.log("base map changed to " + e.name);
});
Because using L.marker().bindPopup() creates the popup content right there and does not support callbacks, you must manually create the popups in response to click event by calling map.openPopup() with your dynamic html (dynamic because it uses a variable: the active basemap name)
marker.on("click", function(e) {
var html = "Current base layer: <br/><b>" + map.activeBaseLayer.options.name + "<b>";
map.openPopup(html,
e.latlng, {
offset: L.point(1, -24)
});
});
Here is a working example on JS fiddle: http://jsfiddle.net/4caaznsc/
Working code snippet also below (relies on Leaflet CDN):
// Create the map
var map = L.map('map').setView([39.5, -0.5], 5);
// Set up the OSM layer
var baseLayer1 = L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
name: "Base layer 1"
});
var baseLayer2 = L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
name: "Base layer 2"
});
// add some markers
function createMarker(lat, lng) {
var marker = L.marker([lat, lng]);
marker.on("click", function(e) {
var html = "Current base layer: <br/><b>" + map.activeBaseLayer.options.name + "<b>";
map.openPopup(html,
e.latlng, {
offset: L.point(1, -24)
});
});
return marker;
}
var markers = [createMarker(36.9, -2.45), createMarker(36.9, -2.659), createMarker(36.83711, -2.464459)];
// create group to hold markers, it will be added as an overlay
var overlay = L.featureGroup(markers);
// show overlay by default
overlay.addTo(map);
// show features
map.fitBounds(overlay.getBounds(), {
maxZoom: 11
});
// make up our own property for activeBaseLayer, we will keep track of this when it changes
map.activeBaseLayer = baseLayer1;
baseLayer1.addTo(map);
// create basemaps and overlays collections for the layers control
var baseMaps = {};
baseMaps[baseLayer1.options.name] = baseLayer1;
baseMaps[baseLayer2.options.name] = baseLayer2;
var overlays = {
"Overlay": overlay
};
// create layers control
var layersControl = L.control.layers(baseMaps, overlays).addTo(map);
// update active base layer when changed
map.on("baselayerchange",
function(e) {
// e.name has the name, but it may be handy to have layer reference
map.activeBaseLayer = e.layer;
map.closePopup(); // any open popups will no longer be correct; take easy way out and hide 'em
});
#map {
height: 400px;
}
<script src="https://npmcdn.com/leaflet#0.7.7/dist/leaflet.js"></script>
<link href="https://npmcdn.com/leaflet#0.7.7/dist/leaflet.css" rel="stylesheet"/>
<div id="map"></div>

how to pass data with marker in leaflet js

I am using leaflet js with openstreetmap in my project.
I have multiple circlemarkers at same place in my map.
I want to store some Id in that circlemarkers so that I can Identify that which data should be refereed when circlemarker is clicked.
My circlemarker is
var myMarker = L.circleMarker(myPoint, { title: 'unselected', radius: 20 });
myMarker.addTo(map);
Here I am using title for other purpose that's why I cant use it.
Can any one tell me some way to do this.
It sounds like you would like to add new functionality (functions, properties, etc) to an existing class. It would make sense to use object-oriented principals for this. For this purpose, I'd recommend you extending the CircleMarker class to add those properties.
customCircleMarker = L.CircleMarker.extend({
options: {
someCustomProperty: 'Custom data!',
anotherCustomProperty: 'More data!'
}
});
Now when you create your circle marker, create an instance of your extended object instead.
var myMarker = new customCircleMarker(myPoint, {
title: 'unselected',
radius: 20,
someCustomProperty: 'Adding custom data to this marker!',
anotherCustomProperty: 'More custom data to this marker!'
});
myMarker.addTo(map);
Now you can get the properties like you would any other option from the marker. This is just a simple case of extending, and you can do more as needed, such as adding other properties or functions to the object.
JSFiddle example: JSFiddle
With the current version of leaflet (0.8-dev) you can just set your custom properties on the marker object itself, without having to create a custom marker class...
function map() {
return L.map('leaflet-canvas',
{
maxZoom: 10,
minZoom: 0,
crs: L.CRS.Simple
});
}
var map = map().setView([0, 0], 10).on('click', onMapClick);
function onMapClick(e) {
var marker = L.circleMarker(e.latlng, {draggable:true});
marker.myCustomID = Math.floor((Math.random() * 100) + 1);
marker.on('click', onMarkerClick);
map.addLayer(marker);
// 'click' the new marker to show the ID when marker created
marker.fireEvent('click');
}
function onMarkerClick(e) {
alert(e.target.myCustomID);
}
Here is a TypeScript friendly way:
DataMarker.ts
import * as L from 'leaflet';
export class DataMarker extends L.Marker {
data: any;
constructor(latLng: L.LatLngExpression, data: any, options?: L.MarkerOptions) {
super(latLng, options);
this.setData(data);
}
getData() {
return this.data;
}
setData(data: any) {
this.data = data;
}
}
SomeOtherFile.ts
import { DataMarker } from './DataMarker';
const marker = new DataMarker([ lat, lng ], anyData, markerOptions);
--
Note 1: I decided not to merge the marker options with the data property
Note 2: Adjust the type of data if you need something more specific
marker is basically javascript object rite.
Below snippet solve my case simply.
var marker = new L.marker([13.0102, 80.2157]).addTo(mymap).on('mouseover', onClick);
marker.key = "marker-1";
var marker2 =new L.marker([13.0101, 80.2157]).addTo(mymap).on('mouseover', onClick);
marker2.key = "marker-2";
function onClick(e) {
alert(this.key); // i can expect my keys here
}
just to complete the picture , to create a handler which will respond to a mouse click on a marker and provide access the new options
function onMarkerClick(e) {
console.log("You clicked the marker " + e.target.options.someCustomProperty);
console.log("You clicked the marker " + e.target.options.anotherCustomProperty);
}
marker.on('click', onMarkerClick);
Try this Uniquely identifying Leaflet Markers , its working for me.
//Handle marker click
var onMarkerClick = function(e){
alert("You clicked on marker with customId: " +this.options.myCustomId);
}
//Create marker with custom attribute
var marker = L.marker([36.83711,-2.464459], {myCustomId: "abc123"});
marker.on('click', onMarkerClick);
I would recommend to structure in your data for your markers in the standard GeoJSON format, which makes it compatible for direct saving as shapefile, etc.
var myMarker = L.circleMarker(myPoint, { title: 'unselected', radius: 20 });
myMarker.properties.id = your_Id;
myMarker.addTo(map);
To retrieve the stored information and do things with it or pass it on to other parts of your program, showing a sample onclick function:
myMarker.on('click',markerOnClick);
function markerOnClick(e) {
my_ID = e.layer.properties.id;
console.log(my_ID, e.latlng);
// do whatever you want with my_ID
}
It took me a while to find out the e.layer.properties way to access the clicked marker's properties, so hope this helps someone. Most other examples only focused on yielding the lat-long of the marker, e.latlng.
Note that you can use this same code even with a whole layer / group of markers. The function will work on each individual marker.
I have a easy solution. options property in each circleMarker is the good place to store custom value.
var myMarker = L.circleMarker(myPoint, { custom_id: 'gisman', radius: 20 });
myMarker.addTo(map);
You can easily retrive the value in options.
function markerOnClick(e) {
var id = e.options.custom_id;
}

Categories

Resources