I'm noob to javascript... I tried to find a solution to a problem on a similar thread, but I don't know why it doesn't work in my case. I can't comment there so I add new question.
All my code is here: Link to Fiddle
When I changed code with example, the map stopped showing.
Where have I done the mistake? Thank You for helping me.
Main part of js is like this:
function myFunction() {
// Add MAP
var map = L.map('map').setView([54.151, 17.823], 9);
var basemap = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
minZoom: 5,
maxZoom: 19
});
basemap.addTo(map);
/* ----------------------------------------------- */
// ZOOMING TO DETECT LOCATION
// map.locate({setView: true, maxZoom: 16});
/* ----------------------------------------------- */
// INFO ABOUT DETECT LOCATION
function onLocationFound(e) {
var radius = e.accuracy;
L.marker(e.latlng).addTo(map)
.bindPopup("Jesteśw odległości " + radius + " [m] od tego punktu.").openPopup();
L.circle(e.latlng, radius).addTo(map);
}
map.on('locationfound', onLocationFound);
// ERROR DETECT LOCATION
function onLocationError(e) {
alert(e.message);
}
// MOUSE CLICK POSITION
var popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("" + e.latlng.toString())
.openOn(map);
}
map.on('click', onMapClick);
/* ----------------------------------------------- */
// MARKERS
// var pkt = L.marker([54.46791, 17.0288]).addTo(map).bindPopup("CIO");
/* ----------------------------------------------- */
function addPoints(data, tabletop) {
for (var row in data) {
var marker = L.marker([
data[row].Latitude,
data[row].Longitude
]).addTo(map);
marker.bindPopup('<strong>' + data[row].Info + '</strong>');
}
}
function init() {
Tabletop.init({
key: 'https://docs.google.com/spreadsheets/d/1BTlWo-T636OCGK-tRMHRP55MQ24OwQ-ZF9yOszyppxk/edit?usp=sharing',
callback: addPoints,
simpleSheet: true
})
}
init()
}
The code in JSFiddle doesn't work because the default JSFiddle javascript Load Type setting is On Load. This means that it will load everything that you have in JavaScript tab without you needing to load anything manually.
But your whole thing initializes with onload callback to myFunction() and it will never fire with that setting enabled. You have two possible solutions:
if you don't want to change anything in Fiddle configuration, than you need to remove onload from the body tag and change myFunction() into self-invoking function:
(function () { **body of the function** }())
or just edit the Fiddle settings (change Load type to No wrap - Bottom of ):
I need instead of a basic view in leaflet (it's a map of the world), the satellite view, but I still need to be able to switch between them? How does this work, can anyone explain this to me?
Installed modules:
https://www.drupal.org/project/gmap
https://www.drupal.org/project/leaflet_more_maps
Without using any external plugin :
gpxpod.map = new L.Map('map', {
zoomControl: true
});
var osmUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
var osmAttribution = 'Map data © 2013 <a href="http://openstreetmap'+
'.org">OpenStreetMap</a> contributors';
var osm = new L.TileLayer(osmUrl, {maxZoom: 18, attribution: osmAttribution});
var esriAerialUrl = 'https://server.arcgisonline.com/ArcGIS/rest/services'+
'/World_Imagery/MapServer/tile/{z}/{y}/{x}';
var esriAerialAttrib = 'Tiles © Esri — Source: Esri, i-cubed, '+
'USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the'+
' GIS User Community';
var esriAerial = new L.TileLayer(esriAerialUrl,
{maxZoom: 18, attribution: esriAerialAttrib});
var gUrl = 'http://mt0.google.com/vt/lyrs=s&x={x}&y={y}&z={z}';
var gAttribution = 'google';
var googlesat = new L.TileLayer(gUrl, {maxZoom: 18, attribution: gAttribution});
var baseLayers = {
'OpenStreetMap': osm,
'ESRI Aerial': esriAerial,
'Google map sat': googlesat
}
L.control.layers(baseLayers, {}).addTo(map);
This piece of code adds a standard control to switch between tile provider layers. It includes two satellite tile providers.
More info : http://leafletjs.com/reference-1.0.3.html#control-layers
I made a more detailed example of my question, but #JulienV came close to what I meant, But it wasn't entirely what I wanted, plus I already found his exact answer in another post. What I wanted was to know how to add more or less maps to the view:
var osmLink = 'OpenStreetMap',
thunLink = 'Thunderforest';
var osmUrl = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
osmAttrib = '© ' + osmLink + ' Contributors',
landUrl = 'https://{s}.tile.thunderforest.com/landscape/{z}/{x}/{y}.png',
thunAttrib = '© '+osmLink+' Contributors & '+thunLink;
var osmMap = L.tileLayer(osmUrl, {attribution: osmAttrib}),
landMap = L.tileLayer(landUrl, {attribution: thunAttrib});
var roadmap = L.tileLayer('https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}',{
maxZoom: 20,
subdomains:['mt0','mt1','mt2','mt3']
});
var satellite = L.tileLayer('https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}',{
maxZoom: 20,
subdomains:['mt0','mt1','mt2','mt3']
});
var sat_text = L.tileLayer('https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}',{
maxZoom: 20,
subdomains:['mt0','mt1','mt2','mt3']
});
And then I added each map to the array like this:
if($map_layouts['get_roads']) {
$roads = "\"Roads\": roadmap";
if($baselist == "") {
$baselist = $roads;
} else {
$baselist = $baselist . "," . $roads;
}
}
Then I needed to check if the $baselist wasn't empty:
if($baselist) {
$base_layout = "var baseLayers = {".$baselist."};";
} else {
$base_layout = "";
}
And then of course add the layers to the map:
L.control.layers(baseLayers).addTo(map);
I worked out the code with the google maps included. The osm and thun map was a nice example, but basically what this code does:
First I make if statements in php to see if the roads are included in the button I made. I pass the get_roads inside the array, so that the if statement returns true and then it sets $roads as the roadmap value, which is given in the map examples in the first code of this answer.
Then I check if $baselist isn't empty, because if it's empty it doesn't need a ,, but if it isn't empty, then it gets the $baselist, which would be the previous set value, then a comma and then the new value.
This was the markup of the layers and then I will pass the baseLayers as those values, which will create the maps inside the leaflet view, if the buttons are checked.
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 = ""
}
}
I'm trying to get the latitude and longitude from variable but I'm keep getting Unexpected token ,. I have tried to use var coordinates = 59.328615,13.485699 and var coordinates = '59.328615,13.485699' but I only got the same error. Now I'm out of ideas. What's wrong?!
var coordinate_latitude = 59.328615;
var coordinate_longitude = 13.485699;
var marker;
var gm_map;
function initialize() {
var latlong = new google.maps.LatLng(coordinate_latitude, coordinate_longitude);
var googlemaps_options = {
zoom: 18,
minZoom: 4,
maxZoom: 18,
center: latlong,
mapTypeId: google.maps.MapTypeId.SATELLITE,
streetViewControl: false
}
gm_map = new google.maps.Map(document.getElementById('google-maps'), googlemaps_options);
marker = new google.maps.Marker({
position: latlong,
draggable: true,
map: gm_map
});
}
$(document).ready(function() {
// INITIERA GOOGLE MAPS
initialize();
});
Thanks in advance.
Don't use the ready function to initialize the map. The needs to be done in the load event once the page is rendered. It works fine for me if I remove the jquery and do this instead:
google.maps.event.addDomListener(window, 'load', initialize);
Working example
var coordinates = 59.328615,13.485699 isn't valid JS. It should be something like:
var coordinate_latitude = 59.328615, coordinate_longitude = 13.485699;
However the error message you're reporting doesn't bear any relation to the code you've posted (as pointed out by alestanis). Your current code shouldn't error, or if it does, it must be a different error message than the one you've mentioned in your question.
Thanks to Dr.Molle I got it working. geocodezip had also the correct answer but I'll use Dr.Molles solution (initialize(); in $(document(ready(function() { ...). Here is the working code.