Leaflet: map.locate set maxZoom dynamically - leaflet

It is easy to track a users position and show a position marker with Leaflet
_map.locate({
watch: true,
setView: true,
maxZoom: 13,
enableHighAccuracy: true
})
and some code in the locationfound callback.
However, the user might wish to zoom to a differed level, but when the position maker gets updated the map always zooms back to the value set in the locate maxZoom option.
Is there a way to change the maxZomm value dynamically depending on the zoom level the user has chosen?

Well, after digging a bit in the source it is just as easy as this:
Listen to the zoomend event
_map.on('zoomend', _changeLocateMaxZoom);
and then update the locateOptions maxZoom
function _changeLocateMaxZoom(e) {
if (_map._locateOptions) {
_map._locateOptions.maxZoom = _map.getZoom();
}
}
Leaflet is a well designed library.

Related

Leaflet zoom control don't zoom in or out

I have an animation, which starts like this:
Window.map = new L.Map('map', {
zoomControl: false
});
L.control.zoom({position:'topleft'
}).addTo(Window.map);
//add tile layers
var basemapLayer = new
L.TileLayer('https://{s}.tiles.mapbox.com/v4/github.map-xgq2svrz/{z}/{x}/{y}.png?ac$
token: '.....'
});
And then I am adding feature layers set zoom min and max, and it did not work either. I may hide it, change the position, but when I click on the '+' or the '-' nothing happens.
I am using the default css from leaflet.

Leafletjs show a single wrapped map

I want to show a single world map and wrap it around so that each area is shown only once on the screen. Please refer the fiddle http://jsfiddle.net/8m13d6vs/
var osmUrl = 'http://{s}.tile.osm.org/{z}/{x}/{y}.png',
osm = L.tileLayer(osmUrl, {
noWrap: false,
attribution: "<a href='http://openstreetmap.org'>OpenStreetMap</a>"
});
var map = L.map('map').setView([0, 0], 1).addLayer(osm);
In the above fiddle, wrap is on, but the world map is duplicated. I want a single world map, and on left/right mouse drag, it should wrap around. It should be responsive to the area as well. Is there any way to achieve this? Hope my problem statement is understandable.
There's the Leaflet worldCopyJump option:
var map = L.map('map', {worldCopyJump: true}).setView([0, 0], 1).addLayer(osm);
It defaults to false, but setting this to true will copy the contents of the map over as the user pans beyond the map boundaries.
This sounds like a misunderstanding of the definition of "wrap":
In the context of Leaflet's Tile Layer / Grid Layer option noWrap, it says:
Whether the layer is wrapped around the antimeridian. If true, the GridLayer will only be displayed once at low zoom levels.
So it sounds like simply noWrap: true should achieve your objective.
Updated JSFiddle: http://jsfiddle.net/8m13d6vs/3/
BTW you should consider upgrading Leaflet version to 1+

Updating layers in Leaflet / Mapbox

I'm trying to make a mapping visualization in realtime, where I keep getting new points via websockets. The initial plotting these markers on the map seems simple, but I'm not sure what's the right way of updating a layer on Mapbox.
As of now, whenever I get a new point, I remove the old layer, create a new one and then add it on the map. The problem with this approach is that it is slow and for high number of points (>5000) it starts lagging.
// remove layer
if (this.pointsLayer != null) {
map.removeLayer(this.pointsLayer);
}
// build geoJSON
var geoJSON = { "type": "FeatureCollection", "features": [] };
geoJSON["features"] = tweets.map(function(tweet) {
return this.getGeoPoint(tweet);
}.bind(this));
// add geoJSON to layer
this.pointsLayer = L.mapbox.featureLayer(geoJSON, {
pointToLayer: function(feature, latlon) {
return L.circleMarker(latlon, {
fillColor: '#AA5042',
fillOpacity: 0.7,
radius: 3,
stroke: false
});
}
}).addTo(map);
Is there a better way?
You can create an empty GeoJSON layer by passing it a false instead of real data:
//create empty layer
this.pointsLayer = L.mapbox.featureLayer(false, {
pointToLayer: function(feature, latlon) {
return L.circleMarker(latlon, {
fillColor: '#AA5042',
fillOpacity: 0.7,
radius: 3,
stroke: false
});
}
}).addTo(map);
then use .addData to update it as new tweets come in. Something like:
// build geoJSON
var geoJSON = { "type": "FeatureCollection", "features": [] };
geoJSON["features"] = /**whatever function you use to build a single tweet's geoJSON**/
// add geoJSON to layer
this.pointsLayer.addData(geoJSON);
For a single tweet, I guess you could just create a Feature instead of a FeatureCollection, though I don't know whether that extra layer of abstraction would make any difference in terms of performance.
EDIT: Here is an example fiddle showing the .addData method at work:
http://jsfiddle.net/nathansnider/4mwrwo0t/
It does slow down noticeably if you add 10,000 points, and for 15,000 points, it's really sluggish, but I suspect that has less to do with how the points are added that the demands of rendering so many circleMarkers.
If you aren't already, you may want to try using the new Leaflet 1.0 beta, which redraws vector layers faster and is generally much more responsive with large datasets. Compare this 15,000-point example using Leaflet 0.7.5 to the same code using Leaflet 1.0.0b2. Not everything is fixed (popups take a long time to open in both), but the difference in lag time when trying to drag the map is pretty dramatic.
There's no reason to go through the intermediate step of construction a GeoJSON object just so you can add it to the map. Depending on your exact needs, you can do something like this:
tweets.forEach(function(t) {
L.marker(this.getGeoPoint(t)).addTo(map);
}, this);
You should manage the tweets object so it only contains points that are not already visible on the map, though. Deleting all the old markers, just so you can add them again, is of course going to be very slow.
I would take a look at Leaflet Realtime:
Put realtime data on a Leaflet map: live tracking GPS units, sensor data or just about anything.
https://github.com/perliedman/leaflet-realtime

Mapbox: How to continuously wrap a feature layer

Mapbox can continuously wrap tile layers such that you can scroll infinitely to the left and right over a repeating map of the world.
I have added a feature layer to a world map which displays some markers and, for some reason, this feature layer does not also wrap. The markers are only present on the original iteration of the world tiles layer.
I've tried using the noWrap: false options property that controls this characteristic on the tile layer when adding a feature layer, but it doesn't seem to have any effect.
Is there any way to do this?
Use the worldCopyJump option
example:
var map = L.mapbox.map('map', 'your.mapid', {center: [lat, long],
zoom: 4,
worldCopyJump: true
});
Not sure if I understand your question...
You can use
maxBounds: [[-90,-180],[90,180]]
for example
map = L.mapbox.map('map', '<some map id>',{minZoom: 0, maxZoom: 10, maxBounds: [[-90,-180],[90,180]]});
https://www.mapbox.com/mapbox.js/example/v1.0.0/maxbounds/

Layer order changing when turning layer on/off

I have two geoJson layers being loaded - both layers are the same data for testing purposes, but being drawn from two different json files. When I turn the layers on and off in the layer controller, the draw order of the layers change.
Any ideas why this is happening?
I have put my code into a JSFiddle: http://jsfiddle.net/lprashad/ph5y9/10/ and the JS is below:
//styling for watersheds_copy
var Orange = {
"color": "#ff7800",
"weight": 5,
"opacity": 0.65
};
var Water_Orange = L.geoJson(watersheds_copy, {
style: Orange
});
Water_Orange.addData(watersheds_copy);
//these are blue
var Water_blue = L.geoJson(watersheds, {});
Water_blue.addData(watersheds);
//This sets the inital order - last in layer list being on top. Except minimal - tile layer is always on bottom
var map = L.map('map', {
center: [41.609, -74.028],
zoom: 8,
layers: [minimal, Water_Orange, Water_blue]
});
var baseLayers = {
"Minimal": minimal,
"Night View": midnight
};
//This controls the order in the layer switcher. This does not change draw order
var overlays = {
"Water_Orange": Water_Orange,
"Water_blue": Water_blue
};
L.control.layers(baseLayers, overlays).addTo(map);
LP
While searching I happened upon this site that shows some of the Leaflet code:
http://ruby-doc.org/gems/docs/l/leaflet-js-0.7.0.3/lib/leaflet/src/control/Control_Layers_js.html
In it I found this condition for the application of autoZIndex:
if (this.options.autoZIndex && layer.setZIndex) {
this._lastZIndex++;
layer.setZIndex(this._lastZIndex);
}
TileLayer is the only layer type that has a setZIndex function, so apparently autoZIndex only works there.
I'm not sure which annoys me more. This incredible limitation or the fact that Leafet documentation doesn't point it out.
At least on 0.7.2, I had to use bringToFront in the callback of map.on('overlayadd'). autoZIndex: false did not work in my case neither. A comment on this issue may explain the reason.
It's not specific to L.GeoJson layers. As far as I can tell, it's true of all Leaflet layers with layer control. The last layer turned on is simply on top. I don't think this is a bug either. It's predictable behavior which I use and depend on when I'm designing maps with layer control...