MapBox - Add a clusterGroup clickable with Layer Control - leaflet

I'm still learning and I'm a bit stuck. I may be trying to do to much at once. I have a MapBox map working great with a clickable layer menu taken from examples on the MapBox site. I also have a MarkerClusterGroup which also works and is always visible on the map. Is there a way I could somehow have the MarkerClusterGroup clickable on/off just like layers identified in var overlays = { ...
Below is the code that I think needs the help:
var layers = {
Streets: L.mapbox.tileLayer('mapbox.streets').addTo(map),
Satellite: L.mapbox.tileLayer('mapbox.satellite'),
Light: L.mapbox.tileLayer('mapbox.light'),
};
var overlays = {
DataA: L.mapbox.featureLayer().loadURL('/data/ctsnew.geojson'),
DataB: L.mapbox.featureLayer().loadURL('/data/selectZipcodes.geojson'),
};
// Since featureLayer is an asynchronous method, we use the `.on('ready'`
// call to only use its marker data once we know it is actually loaded.
Markers: L.mapbox.featureLayer('examples.map-h61e8o8e').on('ready', function(e) {
// The clusterGroup gets each marker in the group added to it
// once loaded, and then is added to the map
var clusterGroup = new L.MarkerClusterGroup();
e.target.eachLayer(function(layer) {
clusterGroup.addLayer(layer);
});
map.addLayer(clusterGroup);
});
Could be something as simple as misuse of brackets. Thanks in advance.

You have to include your Marker Cluster Group in your overlays object. For example you could instantiate it just before defining overlays, even if your Cluster Group is empty for now.
Then you fill it once it has downloaded its data.
var layers = {
Streets: L.mapbox.tileLayer('mapbox.streets').addTo(map),
Satellite: L.mapbox.tileLayer('mapbox.satellite'),
Light: L.mapbox.tileLayer('mapbox.light'),
};
var clusterGroup = L.markerClusterGroup();
var overlays = {
DataA: L.mapbox.featureLayer().loadURL('/data/ctsnew.geojson'),
DataB: L.mapbox.featureLayer().loadURL('/data/selectZipcodes.geojson'),
Markers: clusterGroup
};
// Since featureLayer is an asynchronous method, we use the `.on('ready'`
// call to only use its marker data once we know it is actually loaded.
L.mapbox.featureLayer('examples.map-h61e8o8e').on('ready', function(e) {
// The clusterGroup gets each marker in the group added to it
// once loaded, and then is added to the map
e.target.eachLayer(function(layer) {
clusterGroup.addLayer(layer);
});
map.addLayer(clusterGroup); // use that line if you want to automatically add the cluster group to the map once it has downloaded its data.
});

Related

How to update geojson markers periodically

What I am trying to do is to use Leaflet with OSM map,
and load data from PHP in GeoJSON format + update periodically.
I can manage to display a map, load data, but do not know how to update points instead of still adding a new ones.
function update_position() {
$.getJSON('link_to_php', function(data) {
//get data into object
var geojsonFeature = JSON.parse(data);
// how to remove here old markers???
//add new layer
var myLayer = L.geoJSON().addTo(mymap);
//add markers to layet
myLayer.addData(geojsonFeature);
setTimeout(update_position, 1000);
});
}
update_position();
have tried mymap.removeLayer("myLayer"); but this seems to now work inside of function. Please help
L.geoJSON extends from LayerGroup which provide a function named clearLayers(docs), so you call that to clear markers from the layer.
Also, it is recommended that you put the layer variable outside the function:
var geoJSONLayer = L.geoJSON().addTo(mymap);
function update_position() {
$.getJSON('link_to_php', function(data) {
//get data into object
var geojsonFeature = JSON.parse(data);
geoJSONLayer.clearLayers();
//add markers to layet
geoJSONLayer.addData(geojsonFeature);
setTimeout(update_position, 1000);
});
}
update_position();

Adding leaflet layerGroup options dynamically

I'm using the leaflet snaking polyline to draw a route on a map with multiple markers, and have it working. However, I want to make the snaking speed a setting that the user can change, so I need to be able to modify the default snaking speed when I add the layerGroup to the layer. To add the layer with the default snaking speed I simply have the following line of code (and it works):
layerGrp.addLayer( endMarker );
layerGrp.addTo(polyLayer).snakeIn();
But I want to really add
layerGrp.addLayer(endMarker, { snakingSpeed: snakingSpeedFromUser } );
layerGrp.addTo(polyLayer).snakeIn();
I've tried what's shown above, and also just
layerGrp.addLayer( {snakingSpeed: snakingSpeedFromUser } );
But both of them give me a runtime error of
Uncaught (in promise) Error: The provided object is not a Layer.
Polylayer is a layergroup so I can show/hide the polylines. Here's where it is added to the map:
var gridLayer = L.layerGroup([]);
var polyLayer = L.layerGroup([]);
var overlayMaps = { "Track": polyLayer, "Grid": gridLayer };
var layerControls = { "Map": mapLayer,
"Track": polyLayer,
"Grid": gridLayer };
How can I add the options to the layerGroup dynamically at runtime?
Thanks......

Getting the bounds of loaded tile of leaflet

Using leaflet is there any way I can get the bounds (NorthEast, SouthWest) of a loaded tile? I want to request the server to load the markers only for a particular tile which is loaded, so that when user is panning/dragging the map he can easily see the new markers on new area.
What you really want to do is a subclass of L.GridLayer. This will allow fine control over loaded/unloaded tiles, and is the best way to use the private L.GridLayer._tileCoordsToBounds() method.
With some basic handling of loaded markers, it should look like:
L.GridLayer.MarkerLoader = L.GridLayer.extend({
onAdd: function(map){
// Add a LayerGroup to the map, to hold the markers
this._markerGroup = L.layerGroup().addTo(map);
L.GridLayer.prototype.onAdd.call(this, map);
// Create a tilekey index of markers
this._markerIndex = {};
},
onRemove: function(map){
this._markergroup.removeFrom(map);
L.GridLayer.prototype.onRemove.call(this, map);
};
createTile: function(coords, done) {
var tileBounds = this._tileCoordsToBounds(coords);
var tileKey = this._tileCoordsToKey(coords);
var url = ...; // Compute right url using tileBounds & coords.z
fetch(url).then(function(res){
if (!key in this._markerIndex) { this._markerIndex[key] = []; }
// unpack marker data from result, instantiate markers
// Loop as appropiate
this._markerGroup.addLayer(marker);
this._markerIndex[key] = marker;
done(); // Signal that the tile has been loaded successfully
});
},
_removeTile: function (key) {
for (var i in this._markerIndex[key]) {
this._markerGroup.remove(this._markerIndex[key][i]);
}
L.GridLayer.prototype._removeTile.call(this, key);
}
});
Please note that zooming might be a source of bugs and graphical glitches (markers being removed because a tile unloads, before the markers at the new zoom level are loaded). Beware of that.

Delete feature from multiple layers with Leaflet.draw

There are multiple layers displayed in a map and I'd like to have a delete button to delete selected markers from these layers.
I am using Leaflet.draw plugin to implement that, but it seems that the draw control has to be bind to a single layer whereas I have multiple layers.
I tried to copy all features in layers to a FeatureGroup and bind the FeatureGroup with drawing control to accomplish that, but it broke the Layers control behavior since I cannot control the visibility of layers anymore. (Duplicated features in FeatureGroup).
Is there any way to enable multiple layer feature deleting without breaking layer visibility control?
http://jsfiddle.net/mdqvpLvx/7/
var layerControl = new L.Control.Layers({}, {});
var geojsonLayer = L.geoJson(data);
geojsonLayer.getLayers()[0].addTo(drawnItems);
var geojsonLayer2 = L.geoJson(data2);
geojsonLayer2.getLayers()[0].addTo(drawnItems);
map.addControl(layerControl);
layerControl.addOverlay(geojsonLayer, 'layer1');
layerControl.addOverlay(geojsonLayer2, 'layer2');
I think I still have to keep copies of the markers in FeatureCollection layer and delete them accordingly from particular layers in drawDelete event. And add/remove markers from FeatureCollection layer when layers visibility changes from LayerControl.
// Draw Control
var editableFeatures = L.featureGroup();
map.addLayer(editableFeatures);
var drawControl = new L.Control.Draw({
draw: false,
edit: {
edit: false,
featureGroup: editableFeatures
}
});
map.on('draw:deleted', function(e) {
var layers = e.layers;
layers.eachLayer(function(feature) {
map.eachLayer(function(mapLayer) {
if (mapLayer.hasLayer) {
mapLayer.removeLayer(feature);
}
});
});
});
map.addControl(drawControl);
//LayerControl
var layerControl = new L.Control.Layers({}, {});
map.addControl(layerControl);
map.on('overlayadd', function(layer, name) {
layer.layer.eachLayer(function(feature) {
editableFeatures.addLayer(feature);
});
});
map.on('overlayremove', function(layer, name) {
layer.layer.eachLayer(function(feature) {
editableFeatures.removeLayer(layer);
});
});

Leaflet: Removing markers from map

I load some lat / lon info, then use it to build a polyline.
I then want to add a marker at each of the polyline vertices that will show when the polyline is clicked.
The vertices should disappear if a different (or the same) polyline is clicked.
The code below creates the polyline and the vertex markers.
But the vertex markers do not ever disappear.
I've tried to do this several ways with the same result. I've tried storing the vertex markers in an array and adding them directly to the map, then map.removeLayer'ing them. That doesn't work either. Nor does it work if I use an L.featureGroup instead of a layerGroup.
Clearly I've missed the point somewhere as to how markers can be removed. Could someone point me at the error in my methodology?
// trackMarkersVisible is a global L.layerGroup
// success is a callback from an ajax that fetches trackData, an array f lat/lon pairs
success: function (trackData) {
// build an array of latLng's
points = buildTrackPointSet(trackData, marker.deviceID);
var newTrack = L.polyline(
points, {
color: colors[colorIndex],
weight: 6,
clickable: true
}
);
$(newTrack).on("click", function () {
trackMarkersVisible.clearLayers();
$.each(points, function(idx, val) {
var tm = new L.Marker(val);
trackMarkersVisible.addLayer(tm);
});
map.addLayer(trackMarkersVisible);
});
}
Without a JSFiddle or Plunker it's hard to say because i'm not sure what behaviour your getting but using the clearLayers() method of L.LayerGroup should remove all layers from that group. I would check in the onclick handler if the group already has layers: group.getLayers().length === 0 If the group is empty, add the markers, if the group has layers use clearLayers. Example in code:
polyline.on('click', function (e) {
if (group.getLayers().length === 0) {
e.target._latlngs.forEach(function (latlng) {
group.addLayer(L.marker(latlng));
});
} else {
group.clearLayers();
}
});
This works for me, see the example on Plunker: http://plnkr.co/edit/7IPHrO?p=preview
FYI: an instance of L.Polyline is always clickable by default so you can leave out the clickable: true