How to get the edge of a polygon drawn on map within Leaflet - leaflet

I am working with Leaflet and Leaflet-Draw in Angular to draw some polygons on the Google Map. How can I implement a listener when the user clicks exactly on the edge of the drawn polygons and get the lat and lng of that edge. I know a similar situation can be implemented with Google Map API like the code below, but I can not find any source to help me implement the same thing in Leaflet.
google.maps.event.addListener(polygon, 'click', function (event) { console.log(event.edge) }
Google Map Documentation: https://developers.google.com/maps/documentation/javascript/reference/polygon#PolyMouseEvent

For those who come across this question: I found a solution myself!
I didn't find anything directly from Leaflet draw library that I could use, so I defined the problem for myself as a trigonometry problem and solve it that way.
I defined a function in which on polygon click, it converts the event.latlng and loops over polygon.getLatLngs()[0] taking a pair of A and B points. A is the first coordinates, B is the next and if it reaches to the end of array, B will be the first point. Then using Collinear Function of 3 points with x, y, I checked if the clicked x, y has a same slope as point A and B.(has to be rounded it up), if so, I would save that A and B point pair with their latLng information and further used it in my project.
Although this method works, I would appreciate if anybody would know a better solution or library built-in function that can be used instead. Thanks!

When the user clicks on the polygon you can loop through all corners and check if he clicked in the near of the corner.
poly.on('click', function(e){
var latlng = e.latlng;
var corners = poly.getLatLngs();
if(!L.LineUtil.isFlat(corners)){ //Convert to a flat array
corners = corners[0];
}
//Convert the point to pixels
var point = mymap.latLngToContainerPoint(latlng);
//Loop through each corner
corners.forEach(function(ll){
//Convert the point to pixels
var point1 = mymap.latLngToContainerPoint(ll);
var distance = Math.sqrt(Math.pow(point1.x - point.x, 2) + Math.pow(point.y - point1.y, 2));
//Check if distance between pixels is smaller then 10
if(distance < 10){
console.log('corner clicked');
}
});
});
This is plain JS you have to convert it self to angular.
A alternativ is to place on each corner a DivMarker or a CircleMarker and fire a event if the marker is clicked.
Looks like: https://geoman.io/leaflet-geoman

Related

Why does my Leaflet circle only show as a dot?

I'm trying to add circles to my map, but for some reason the circles only show as dots, irrespective of the radius size.
var circle = L.circle(map.unproject([9541, 7658], map.getMaxZoom()), {
radius: 500
}).addTo(map);
I'm using pixel coordinates, but as you can see I'm converting them, so even though I only get dots on the map, they show at the right coordinates. I would hope this isn't the issue, but...?
I've successfully added circleMarkers, but the radius doesn't grow when zooming. At least not that I could see.
So the question is: how can I get the dots to show as circles?
Using Leaflet 1.9.3
Update
It appears that with pixel coordinates you need to enter a really high value for the radius. Thought I had already tried this before asking the question but apparently not.
var circle = L.circle(map.unproject([9541, 7658], map.getMaxZoom()), {
radius: 50000
}).addTo(map);
Unfortunately they're all showing at different sizes, even with the same radius, but that's a different question...
I originally misread your question and gave an incorrect answer, sorry. Circle radius is static. I think that the best way to change it with zoom level would be to use a zoom event listener:
let currZoom = map.getZoom();
let circles = [/* Store your circles here as you create them */];
map.on("zoomend", () => {
const zoomDiff = map.getZoom() - currZoom;
currZoom = map.getZoom();
for (const circle of circles) {
circle.setRadius(circle.getRadius() * 2 ** zoomDiff);
}
});
It's been a while since I've worked with Leaflet, but I think that will do the trick.
Edited to account for your comment regarding multiple circles.

leaflet editable restrict draw to a specific area

In Leaflet.Editable I want to confine/limit my customers to draw only in a specific area/bounds.
actually im trying to limit them to (90, -90, 180, -180) bounds of map..
maxBounds: [[-90, -180], [90, 180]]
I was not able to find anything anywhere and it seems that i am missing something.
CODEPEN DEMO
please help.
EDIT:
the Y axis is blocking correctly and mouse cannot stretch shape beyond top and bottom.
the problem is in X axis (as seen in pictures)
as for now i solved it with after save check and clear shape if it out of map bounds (BAD USER EXPERIENCE). i need a mouse confinement just like y axis does.
Without knowing your use case (why the whole world map??) Quickest and easiest fix would be to simply set the map's minZoom to something a bit higher, for example, I found that minZoom: 5 was adequate except for cases where the map was both really short and really wide (which is rarely the case in most apps I've seen).
But the real fix involves writing your own custom overrides for dragging markers and shapes.
According to API doc the L.Editable plugin allows you to override a bunch of stuff including the VertexMarker class, via map.editTools.options.vertexMarkerClass.
Fixed codepen: http://codepen.io/anon/pen/GrPpRY?editors=0010
This snippet of code that allows you to constrain the longitude for dragging vertex markers by correcting values under -180 and over 180 is this:
// create custom vertex marker editor
var vertexMarkerClass = L.Editable.VertexMarker.extend({
onDrag: function(e) {
e.vertex = this;
var iconPos = L.DomUtil.getPosition(this._icon),
latlng = this._map.layerPointToLatLng(iconPos);
// fix out of range vertex
if (latlng.lng < -180) {
e.latlng.lng = latlng.lng = -180;
this.setLatLng(latlng);
}
if (latlng.lng > 180) {
e.latlng.lng = latlng.lng = 180;
this.setLatLng(latlng);
}
this.editor.onVertexMarkerDrag(e);
this.latlng.update(latlng);
this._latlng = this.latlng; // Push back to Leaflet our reference.
this.editor.refresh();
if (this.middleMarker) this.middleMarker.updateLatLng();
var next = this.getNext();
if (next && next.middleMarker) next.middleMarker.updateLatLng();
}
});
// attach custom editor
map.editTools.options.vertexMarkerClass = vertexMarkerClass;
I didn't code for dragging the shape as a whole (the rectangle, in this case). While the VertexMarker fix should address all kinds of vertex dragging, you need to override each shape's drag handler to properly constrain the bounds. And if bounds are exceeded, crop the shape appropriately. As was pointed out, Leaflet already does this for latitude, but because Leaflet allows wrapping the map around horizontally you have your essential problem. Using rec.on("drag") to correct the bounds when they cross over your min/max longitude is the only way to address it. It is basically the same solution as I have laid out for the vertexMarkerClass - actual code left as exercise for the diligent reader.

Leaflet latLngToContainerPoint and containerPointToLatLng not reciprocal?

Anybody know why the following is not reciprocal? latLng and new
var point = dispmap.latLngToContainerPoint(latlng);
var newPoint = L.point([point.x, point.y]);
var newLatLng = dispmap.containerPointToLatLng(newPoint);
When I execute this code I send in latlng=(26.75529,-80.93581)
newLatLng, which by inspection of the code above I would expect to reciprocate gives back...
newLatLng = (26.75542,-80.93628)
I'm wanting to array some markers with identical lat-lons around the shared spot on a map, and bumping each by some screen coordinates looks like the best method based on some blog/issue reading I've done.
I'm, "close" to what I want to achieve, but as I try to validate what these leaflet calls are doing for me I hit the fundamental question above.
They can't be ...
Latitude and longitude are float values while x and y are integer values.
This means that there are an (theoretically) infinite number of latlng's and a rather small number of points on your view (width * height).
Furthermore, I'm not sure how you define identical latlng's; the best you can't to is to speak of proximity.
If I read between the lines, identical may mean that the markers overlap. Then the best way is to have a look how Leaflet.MarkerCluster are tackling with the problem.
I was able to achieve my desired result by altering zoom level to avoid pixel-point quantization effects on my translations. The screenshot below illustrates an orange and two green circle markers that represent an identical lat-lon, but I want the green arrayed around the orange in a circular fashion...in this example there are only 2 green.
I perform simple circular array math with an angular step size of PI/4 in this example. The KEY to getting the visual effect correct is the "dispmap.setZoom(dispmap._layersMaxZoom)" call BEFORE I do the math, and then I invoke "dispmap.setZoom(mats.zoom)" after the math, which will give the user the desired zoom level as specified by variable mats.zoom.
var arrayRad=20;
var dtheta=Math.PI/4;
var theta=0;
dispmap.setZoom(dispmap._layersMaxZoom)
L.geoJson(JSON.parse(mats.intendeds), {
pointToLayer: function (feature, latlng) {
var point = dispmap.latLngToContainerPoint(latlng);
dx = arrayRad*Math.cos(theta);
dy = arrayRad*Math.sin(theta);
theta += dtheta;
var newPoint = L.point([point.x + dx, point.y+ dy]);
var newLatLng = dispmap.containerPointToLatLng(newPoint);
return L.circleMarker(newLatLng, intendedDeliveryLocationMarkerOptions);
}, onEachFeature: onEachIntendedLocFeature }).addTo(dispmap);
dispmap.setZoom(mats.zoom);
Sample screen shot at max zoom level: 2 arrayed markers

Unable to display only the points within a specific range (circle) using the .getBounds() function (Leaflet)

I am trying to display a certain amount of points within a specific range, that is within a circle. But when using the .getBounds() function for comparison to see whether the point is within the bound, i get some points outside it as shown in the screenshot below:
Map Screenshot
The code currently using to check if the point is within the circle bound is below:
echo '
var mark = L.marker([' . $r->coordinates[0]->longitude . ',' . $r->coordinates[0]->latitude . ']);
if(circle.getBounds().contains(mark.getLatLng())){
mark.addTo(map);
mark.bindPopup("'.$info.'");
}
';
I am looping into an array to retrieve the latitude and longitude and from there, to see whether the coordinates fills into the bound, if so, it adds it to the map with their corresponding popup
Any solution regarding this particular issue?
Thanks for helping
You can create your own contains method and add it to the L.Circle class because it doesn't have one by default. You can use the utility method distanceTo of the L.LatLng objects to calculate distance between your marker and the circle's center and compare that to the circle's radius:
L.Circle.include({
contains: function (latLng) {
return this.getLatLng().distanceTo(latLng) < this.getRadius();
}
});
Now when you have a circle and a marker or latlng object you can do this:
var map = L.map(...);
var circle = L.circle(...).addTo(map),
marker = L.marker(...).addTo(map);
latLng = L.latLng(...);
// Returns true when in the circle and false when outside
circle.contains(marker.getLatLng());
circle.contains(latLng);
Working example on Plunker: http://plnkr.co/edit/OPF7DM?p=preview
L.Circle reference: http://leafletjs.com/reference.html#circle
L.Marker reference: http://leafletjs.com/reference.html#marker
L.LatLng reference: http://leafletjs.com/reference.html#latlng
The method getBounds() always returns a rectangular area. Hence it can't be used for checking whether a non-rectangular object contains a given point.
For a circle you should be able to calculate the distance (distanceTo()) of the point to the circle's center (getLatLng()) and check whether it is smaller than the circle's radius (getRadius()). Note that the distance and radius are in meters.

How can I draw a polygon on intersection coordinates on Nutiteq?

Hey people this is going to be my first question so dont hit me too hard !
Before I have already added polygons but the intersection is a bit complicating.
with pre-defined i mean for example intersection coordinates of two other polygons. I'm calculating the area of the polygon intersection but i also want to highlight the area. Thanks
You would need two steps:
calculate intersection: polygon from 2 polygons. I would use JTS for it, you would need to provide data in JTS objects.
highlight the intersection on mapview (nutiteq for example). You can just add the resulting polygon as one geometry element into geometry layer, just as any other polygon. Use special styling to make it look different. You would need to convert JTS polygon to Nutiteq Polygon object to show it on map
ArrayList<MapPos> keslist = new ArrayList<MapPos>();
for (int i = 0; i < sonuc.getNumPoints(); i++) {
double lon = sonuc.getX(i);
double lat = sonuc.getY(i);
MapPos mPos = new MapPos(lon, lat);
keslist.add(mPos);
}
PolygonStyle polygonStyle = PolygonStyle.builder().setColor(Color.GREEN).build();
StyleSet<PolygonStyle> polygonStyleSet = new StyleSet<PolygonStyle>(null);
polygonStyleSet.setZoomStyle(10, polygonStyle);
Polygon KesisimPol = new Polygon(keslist, new DefaultLabel("Kesişim"), polygonStyleSet, null);
GeometryLayer geomLayer = new GeometryLayer(mapView.getLayers().getBaseLayer().getProjection());
mapView.getLayers().addLayer(geomLayer);
geomLayer.add(KesisimPol);
}
Here is my solution. I've tried it works. Right now I'm trying to add this new polygon to editable objects layer. Because I can't use the result polygon in another intersection process.
I hope this will help the others.