I use this plugin to draw lines on Leaflet map in my project:
https://github.com/alex3165/react-leaflet-draw
I want to get coordinates (lat,long) of drawn polyline, when _onCreate or later. How could i do this?
I agree, the documentation is a bit lacking, so I hope this helps anyone else trying to do this. You first implement an onCreated function that is registered on the <EditControl> component. You can then access the current layer object and its layerType from the event object that gets passed to this callback.
Depending on the shape type, you can access the coordinates of the shape via a the methods provided by Leaflet (e.g. circle). See code below.
export default DrawTool({prop1, prop2) {
const onCreated = (e) => {
if (e.layerType === 'circle') {
console.log("circle center, radius =", e.layer.getLatLng(), e.layer.getRadius())
} else {
// polygon & rectangle
console.log("polygon coordinates =", e.layer.getLatLngs()) // array of LatLng objects
} // marker or lines, etc.
// map.addLayer(e.layer) // might need?
}
const onDelete = (e) => {
// do something with e.layer
}
return (
<EditControl
position='topright'
onCreated={onCreated}
onDeleted={onDeleted}
...
/>
)
}
Related
I have a click event on a map feature that zooms into that feature when clicked by the user
Map starts like this:
When a user clicks on the map feature:
L.geoJson(geoJsonFeatureCollection, {
style,
onEachFeature
}).addTo(map)
function zoomToFeature (e) {
map.fitBounds(e.target.getBounds())
}
function onEachFeature (feature, layer) {
layer.on({
click: zoomToFeature
})
}
On a second click on the same map feature, after being already zoomed in, I'd like to forward the URL to another page (the info HTML page of that feature). But I can't distinguish both situations.
How can I know in map.fitBounds if the map bounds were already fitted, i.e., if the method actually did/zoomed/panned anything?
You can use the same calculation function from leaflet and check if it equals to the current map state:
function zoomToFeature (e) {
const bounds = e.target.getBounds();
const target = map._getBoundsCenterZoom(bounds);
if(target.zoom === map.getZoom() && map.getCenter().equals(target.center)){
// bounds already fitting
} else {
map.fitBounds(bounds);
}
}
Maybe you need to change the margin of equals. Default: .equals(target.center, 1.0E-9)
I am trying to draw the path of a flight using leafletjs and geojson. I'll be getting the geometry from a stream.
this is what I have done so far:
let index = 0;
let geoJsonLayer;
let intervalFn = setInterval(function () {
let point = trackData.features[index++];
if(point) {
let coords = point.geometry.coordinates;
coords.pop();
coords.reverse();
geoFeature.geometry.coordinates.push(coords);
if(map.hasLayer(geoJsonLayer)) map.removeLayer(geoJsonLayer);
geoJsonLayer = L.geoJson(geoFeature, {
onEachFeature: (feature, layer) => {
const content = feature.properties.title;
layer.bindPopup(content);
}
});
geoJsonLayer.addTo(map);
// console.log(coords);
} else {
clearInterval(intervalFn);
}
}, 100);
setInterval is to simulate the part whereby I get the geometry from a stream.
now when a user clicks on the path I need to show some properties of the path, and I am trying to use the onEachFeature for that, but its not working correctly.
I suspect its because I am removing the layers (I did this to improve the performance)
Is there any other better ways to do what I am trying to achieve ?
You should probably try addLatLng()
Adds a given point to the polyline.
Your geoFeature sounds to be a single Feature, so your geoJsonLayer will contain a single layer (polyline):
let myPolyline;
geoJsonLayer.eachLayer(function (layer) {
myPolyline = layer; // Will be done only once actually.
});
// When you receive a new point…
myPolyline.addLatLng([lat, lng]);
With this you should not have to remove your layers every time.
The popup should therefore stay open, if it is shown.
Demo: https://jsfiddle.net/3v7hd2vx/265/ (click on the button to add new points)
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.
When a draw:editvertex event fires, how can I get information about the polygon which triggered it?
this.map.on('draw:editvertex', function (e) { debugger;
var layers = e.layers;
// I want to get current polygon latLng here
}.bind(this));
This approach works for me (but doesn't feel like best practice) –
In my draw:editvertex handler I loop through the target._layers and look for the edited property:
map.on('draw:editvertex', function(e) {
for (thisLayer in e.target._layers) {
if (e.target._layers.hasOwnProperty(thisLayer)) {
if (e.target._layers[thisLayer].hasOwnProperty("edited")) {
console.log("we think we found the polygon?");
console.log(e.target._layers[thisLayer]);
// the updated Polygon array points are here:
newPolyLatLngArray = e.target._layers[thisLayer].editing.latlngs[0];
}
}
};
});
...like I said, this doesn't feel Awesome, but it is working for me so far.
There are not only layers in e, but also the target layer poly can be approached easily.
map.on('draw:editvertex', function (e) {
var poly = e.poly;
var latlngs = poly.getLatLngs(); // here the polygon latlngs
});
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