I have a problem with Leaflet that actually holds up my whole work. For some reasons I can not explain, the UI of Leaflet is correctly loaded in my Intel XDK app, but there is only one map tile loaded - the same code works in another test app! Now, that I tried everything I could do, I hope that someone here can solve my problem.
For better understanding, here is the code in my leaflet.js (it isn't the leaflet.js, because I'm using the leaflet-src.js as script) and a screenshot of the map window of the app.
function initLeaflet() {
document.getElementById("map").setAttribute("style", "height:" + window.innerHeight + "px; width:" + window.innerWidth + "px;");
var map = L.map('map');
L.tileLayer('https://{s}.tiles.mapbox.com/v3/{id}/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data © OpenStreetMap contributors, ' +
'CC-BY-SA, ' +
'Imagery © Mapbox',
id: 'examples.map-i875mjb7'
}).addTo(map);
map.on('locationfound', onLocationFound);
map.on('locationerror', onLocationError);
map.locate({setView: true, maxZoom: 16});
map.on('click', onMapClick);
}
function onLocationFound(e) {
var radius = e.accuracy / 2;
L.marker(e.latlng).addTo(map)
.bindPopup("Position: " + e.latlng + " Genauigkeit " + radius ).openPopup();
L.circle(e.latlng, radius).addTo(map);
}
function onLocationError(e) {
alert(e.message);
}
function onMapClick(e) {
marker = new L.marker(e.latlng, {id:uni, icon:redIcon, draggable:'true'});
marker.on('dragend', function(event){
var marker = event.target;
var position = marker.getLatLng();
alert(position);
marker.setLatLng([position],{id:uni,draggable:'true'}).bindPopup(position).update();
});
map.addLayer(marker);
}
//var x = document.getElementById("demo");
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
//x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function showPosition(position) {
//x.innerHTML = "Latitude: " + position.coords.latitude +
//"<br>Longitude: " + position.coords.longitude;
}
http://imgur.com/exOUZuT
I would guess that the size of the map upon initialization is the culprit.
Leaflet needs to know the size of the element it is embedded in when initializing. Leaflet uses that information to know how much tiles to load etc. Furthermore any programmatic changes (or changes that cannot be easily detected by Leaflet) to the size of the map have to be followed by map.invalidateSize(..) link.
I suspect that after you set the size, Leaflet fails to read properly the new size of the #map element. Try invalidating the size afterwards or run initialization asynchronously. I would add:
setTimeout(function () {
map.invalidateSize();
}, 0);
and check if it gets any better.
I used that command to fix my missing tiles problem:
map.getSize();
Look like Leaflet needs to know the size of the element map in advance as Michal said.
Related
Alright, so I've tried and failed a number of times and dug through SE hoping to figure out my problems.
I've been basing a lot of my work off this SE post: link.
I have been unable to make this work, mainly because of two errors, but the first one being an obvious hurdle to overcome first:
Error 1:
Uncaught SyntaxError: Unexpected token <
Error 2:
Uncaught TypeError: L.markerClusterGroup.layerSupport is not a function
So, I would like clustering to work with any layer that is turned on via the L.control.layers() function.
Here is my page, as it sits now: TN Alcohol Map
And the code, sans headers/misc:
// initialize the map
var map = L.map('map').setView([36.17, -86.78], 7);
// load a tile layer/base map
L.tileLayer('http://stamen-tiles-{s}.a.ssl.fastly.net/terrain/{z}/{x}/{y}.png',
{
attribution: 'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap',
maxZoom: 18,
minZoom: 7
}).addTo(map);
//attributes for basemap credit (lower right hand corner annotation)
var streetsAttr = 'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap';
var aerialAttr = 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community';
var artsyfartsyAttr = 'Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap';
//crete variables for the base map layer switcher
var streets = L.tileLayer('http://stamen-tiles-{s}.a.ssl.fastly.net/terrain/{z}/{x}/{y}.png', {id: 'MapID', attribution: streetsAttr}),
aerial = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {id: 'MapID', attribution: aerialAttr}),
artsyfartsy = L.tileLayer('http://stamen-tiles-{s}.a.ssl.fastly.net/toner/{z}/{x}/{y}.png', {id: 'MapID', attribution: artsyfartsyAttr});
//create baseMaps variable to store basemap layer switcher
var baseMaps = {
"Streets": streets,
"Aerial": aerial,
"ArtsyFartsy": artsyfartsy
};
// BEER icon & load beer geojson
var beerIcon = L.icon({
iconUrl: 'glass.png',
iconSize: [24, 48]
});
var beerMarker = L.geoJson(false, {
pointToLayer: function(feature, latlng) {
var marker = L.marker(latlng, {
icon: beerIcon
});
//popup shows NAME, ADDRESS, URL and opens the URL in a new window/tab
marker.bindPopup("<strong>" + feature.properties.NAME + "</strong><br/>" + feature.properties.STREETNUM + " " + feature.properties.STREET + ", " + feature.properties.CITY + "<br/>" + "<a target = _blank href=" +
feature.properties.URL + ">" + feature.properties.URLDISPLAY + "</a>");
return marker;
}
}).addTo(map);
$.getJSON("breweries.geojson", function(data) {
beerMarker.addData(data);
});
// WINE icon & load wine geojson
var wineIcon = L.icon({
iconUrl: 'wine.png',
iconSize: [48, 48]
});
var wineMarker = L.geoJson(false, {
pointToLayer: function(feature, latlng) {
var marker = L.marker(latlng, {
icon: wineIcon
});
//popup shows NAME, ADDRESS, URL and opens the URL in a new window/tab
marker.bindPopup("<strong>" + feature.properties.NAME + "</strong><br/>" + feature.properties.STREETNUM + " " + feature.properties.STREET + ", " + feature.properties.CITY + "<br/>" + "<a target = _blank href=" +
feature.properties.URL + ">" + feature.properties.URLDISPLAY + "</a>");
return marker;
}
}).addTo(map);
$.getJSON("wine.geojson", function(data) {
wineMarker.addData(data);
});
//Define overlay maps (non-base layer maps)
var overlayMaps = {
"Breweries": beerMarker,
"Wineries": wineMarker
};
//Creates a Marker Cluster Group
var mcg = L.markerClusterGroup.layerSupport().addTo(map);
//Checking in the 'sub groups'
mcg.checkIn([
beerMarker,
wineMarker
]);
//baseMaps layer switcher
L.control.layers(baseMaps, overlayMaps).addTo(map);
As said by nathansnider in the question comment, the content of your leaflet.markercluster.layersupport-src file is not the JavaScript code of the markerCluster.LayerSupport plugin, but the GitHub HTML page that displays the code of that file, i.e. surrounded by plenty HTML code.
You should simply replace the content of your file by the content of the raw file here: https://raw.githubusercontent.com/ghybs/Leaflet.MarkerCluster.LayerSupport/master/leaflet.markercluster.layersupport-src.js
Demo: http://plnkr.co/edit/Jd8skZ1U0bWxgl2orJV6?p=preview
Side note:
If you just need the Layers Control to work with Leaflet.markercluster, there is another plugin that does just that and which is much simpler: Leaflet.FeatureGroup.SubGroup (230 lines of code v.s. 600 lines for Leaflet.MarkerCluster.LayerSupport as of today).
In your case you would use it this way:
// Create a normal Marker Cluster Group.
var mcg = L.markerClusterGroup().addTo(map);
// Create SubGroups.
var beerMarkerSub = L.featureGroup.subGroup(mcg).addTo(map);
var wineMarkerSub = L.featureGroup.subGroup(mcg).addTo(map);
// For Layers Control.
var overlayMaps = {
"Breweries": beerMarkerSub,
"Wineries": wineMarkerSub
};
// That is it! No need to check-in.
Code specific for your application, since you load GeoJSON data by AJAX:
var beerMarker = L.geoJson(null, beerOptions); // DO NOT add to map.
var wineMarker = L.geoJson(null, wineOptions); // Same story.
$.getJSON("breweries.geojson", function(data) {
beerMarker.addData(data); // GeoJSON conversion.
// Then transfer all features into the corresponding sub-group.
beerMarker.eachLayer(function (layer) {
layer.addTo(beerMarkerSub);
});
});
$.getJSON("wine.geojson", function(data) {
wineMarker.addData(data); // GeoJSON conversion.
// Then transfer all features into the corresponding sub-group.
wineMarker.eachLayer(function (layer) {
layer.addTo(wineMarkerSub);
});
});
Demo: http://plnkr.co/edit/x8vTLjE53TPiLd52BQ1d?p=preview
Disclosure: I am the author of these plugins.
I am looking for solution to add filter (not checkbox) to my site. I have this code loading blank map from Mapbox and added Markers from JSON file. I was trying to add setFilter function, but probably I am using it wrong. I would like to filter items by category property from my JSON file.
<script>
L.mapbox.accessToken = '*************';
var baseLayer = L.mapbox.tileLayer('test****');
var markers = L.markerClusterGroup();
// CALL THE GEOJSON HERE
jQuery.getJSON("locations.geojson", function(data) {
var geojson = L.geoJson(data, {
onEachFeature: function (feature, layer) {
// USE A CUSTOM MARKER
layer.setIcon(L.mapbox.marker.icon({'marker-symbol': 'circle-stroked', 'marker-color': '004E90'}));
// ADD A POPUP
layer.bindPopup("<h1>" + feature.properties.title + "</h1><p>" + feature.properties.description + "</p><p><a href=' + feature.properties.website + '>" + feature.properties.website + "</a></p>");
layer.on('mouseover', function (e) {
this.openPopup();
});
layer.on('mouseout', function (e) {
this.closePopup();
});
}
});
markers.addLayer(geojson);
// CONSTRUCT THE MAP
var map = L.map('map', {
searchControl: {layer: markers},
zoom: 6,
center: [51.505, -0.09],
maxZoom: 13
})
.setView([62.965, 19.929], 5)
.fitBounds(markers.getBounds());
baseLayer.addTo(map);
markers.addTo(map);
L.control.fullscreen().addTo(map);
});
</script>
Could you please help me add filter buttons (something like here: https://www.mapbox.com/mapbox.js/example/v1.0.0/filtering-markers)
PS: I think I tried all examples from Mapbox website, yet seems my skills are very limited here.
Thank you
I was trying to add setFilter function, but probably I am using it wrong. I would like to filter items by category property from my JSON file.
This code example is using L.geoJson to load the markers into your map. Like the Mapbox example, you'll need to use L.mapbox.featureLayer instead, since it includes the setFilter function and L.geoJson does not.
tmcw's answer is correct, L.mapbox.featureLayer is confusing
this tutorial helped me!
https://www.mapbox.com/mapbox.js/example/v1.0.0/custom-marker-tooltip/
I'm using this http://blog.carte-libre.fr/index.php?post/2012/02/12/Serve-all-MBTtile-features-with-PHP-script to create a web map with mbtiles hosted on my server.
I want to create selectable layers using several mbtiles (mb1, mb2, mb3) which are stored on different servers (serv1, serv2, serv3).
The script is
wax.tilejson(
'mbtiles-server.php?db=mb1.mbtiles',
function(tilejson) {
var omq = new L.TileLayer(
'http://otile2.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png', {
maxZoom: 14,
attribution: 'OpenStreetMap - MapQuest',
opacity: 0.4,
});//modify to call mb2 from serv3
var power = new L.TileLayer(
"mbtiles-server.php?db=mb1.mbtiles&z={z}&x={x}&y={y}", {
maxZoom: 14,
attribution: 'OpenStreetMap - CL 2012-02-05',
});
var map = new L.Map('map', {
center: new L.LatLng(46, 0),
zoom: 6,
layers: [omq, power]
});
map.addControl( new L.Control.Layers( { "OpenMapQuest": omq }, { "Power": power }));
wax.leaf.interaction(map, tilejson);
document.getElementById("legend").innerHTML = tilejson.legend;
});
Assuming there is a php script file in each mbtiles folder, how can I modify the script to be able to call mb2 from serv3 so that i have 2 layers from 2 mbtiles hosted on 2 servers?
any advice would be welcomed!
An update...
The issue was trying to load several mbtiles while having respective popup interactions for each layer (each mbtile included interaction, created in TileMill).
I opted to load the mbtiles to the map using:
var base1 = L.tileLayer("http://ms1.mysite.com/folder1/mbtiles-server.php?db=base1layer.mbtiles&z={z}&x={x}&y={y}.png"
var base2 = L.tileLayer("http://ms2.mysite.com/folder2/mbtiles-server.php?db=base2layer.mbtiles&z={z}&x={x}&y={y}.png"
Then added interaction from a separate geojson file.
var onEachFeature_Polygon = function (feature, layer) {
//polygon geojson file contains center points
var centerLat = feature.properties.y;
var centerLon = feature.properties.x;
var centLatLon = new L.LatLng(centerLat, centerLon); //this is used to place the popup in the "mouseover" function
layer.setStyle(tooltip_default_style);
if (feature.properties && feature.properties.Name) {//fields Name, Population, RI10K
var list = "<b>" + feature.properties.Name + "</b>" + "<br>" + "<i>" + "Population: " + feature.properties.Population + "</i>" + "<br>" + "<i>" + "Income 10,000+: " + feature.properties.RI10K + "</i>" + "<br>";//end of tooltip list
layer.bindPopup(list);
}
layer.on("mouseover", function (e) {
layer.setStyle(tooltip_hover_style);
layer.openPopup(centLatLon);
});
layer.on("mouseout", function (e) { layer.setStyle(tooltip_default_style) });
}
//this is the onEachFeature function called when the statsJSON layer is added to the map
var statsinteractive = new L.GeoJSON(statsgeo, { onEachFeature: onEachFeature_Polygon });
statsinteractive.addTo(map);
Would still like to understand or be directed to a clearly outlined method to 'call' interaction from an mbtile file.
In the meantime, I hope this is helpful for someone with a similar issue.
I'm trying to update the lat/lng value of a marker after it is moved. The example provided uses a popup window to display the lat/lng.
I have a "dragend" event listener for the marker, but when I alert the value of e.latlng it returns undefined.
javascript:
function markerDrag(e){
alert("You dragged to: " + e.latlng);
}
function initialize() {
// Initialize the map
var map = L.map('map').setView([38.487, -75.641], 8);
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © OpenStreetMap contributors, CC-BY-SA',
maxZoom: 18
}).addTo(map);
// Event Handlers
map.on('click', function(e){
var marker = new L.Marker(e.latlng, {draggable:true});
marker.bindPopup("<strong>"+e.latlng+"</strong>").addTo(map);
marker.on('dragend', markerDrag);
});
}
$(document).ready(initialize());
http://jsfiddle.net/rhewitt/Msrpq/4/
Use e.target.getLatLng() to get the latlng of the updated position.
// Script for adding marker on map click
function onMapClick(e) {
var marker = L.marker(e.latlng, {
draggable:true,
title:"Resource location",
alt:"Resource Location",
riseOnHover:true
}).addTo(map)
.bindPopup(e.latlng.toString()).openPopup();
// #12 : Update marker popup content on changing it's position
marker.on("dragend",function(e){
var chagedPos = e.target.getLatLng();
this.bindPopup(chagedPos.toString()).openPopup();
});
}
JSFiddle demo
latlng value is not in e.latlng but in e.target._latlng .
Use console.
While using e.target._latlng works (as proposed by this other answer), it's better practice to use
e.target.getLatLng();
That way we're not exposing any private variables, as is recommended by Leaflet:
Private properties and methods start with an underscore (_). This doesn’t make them private, just recommends developers not to use them directly.
I think the API changed.
Nowadays is: const { lat, lng } = e.target.getCenter();
if anyone is using react than you should use :
const map = useMap()
map.addEventListener("dragend" , ()=> {
const {lat , lng} = map.getCenter()
})
I've been playing around with popups, in conjunction with a geojson wrapped inside JavaScript and have mastered what I need to do on the bindpopup front. Now I'd like to effectively unbind the popup from its marker and get the popup to appear either in a side panel or below the map in its own div.
This is the code for my current popup and I'm guessing that I need to change my code around the area of layer.bindPopup(popupContent) and reference it to its own div?
<script>
var map = L.map('map').setView([51.4946, -0.7235], 11)
var basemap =
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'OSM data',
}).addTo(map);
function onEachFeature(feature, layer) {
var popupContent = "<b>" + feature.properties.ward_names +
" </b><br>Population 2011 = <b>" + feature.properties.census_11 +
" </b><br>Population 2001 = <b>"+ feature.properties.census_01 +
" </b><br>You can find out more about population data on the <a href='http://www.somewhere.com' target='_blank'>somewhere.com</a> website ";
if (feature.properties && feature.properties.popupContent) {
popupContent += feature.properties;
}
layer.bindPopup(popupContent);
}
L.geoJson([wardData], {
style: function (feature) {
return { weight: 1.5, color: "#000000", opacity: 1, fillOpacity: 0 };
return feature.properties;
},
onEachFeature: onEachFeature,
}).addTo(map);
</script>
However I'm not really sure how to do this and need some guidance.
Answer shared under another question, https://gis.stackexchange.com/a/144501/44746
If you're looking for populating another element entirely that you've
defined outside of the map, then you actually don't need even the
whole info control. You can just as easily do what you need in the
onEachFeature >> layer.on >> click part too.
function onEachFeature(feature, layer) {
layer.on({
click: function populate() {
document.getElementById('externaldiv').innerHTML = "BLAH BLAH BLAH " + feature.properties.name + "<br>" + feature.properties.description;
}
}); }
In your html body, you just have to make sure your <div id="externaldiv"> or whatever is placed where you want it.
This demo populates an external when the user clicks on a map
feature : http://labs.easyblog.it/maps/leaflet-geojson-list/ (Edit: link reported broken as of dec 2022)
The L.Control class is the appropriate tool for what you want to do.
I suggest you follow this tutorial, it will give you a quick understanding of what you can do with it.