Mapbox Overlapping Circles - leaflet

Does anyone know a way to make overlapping circles in mapbox show the same color and only have the border around the outer edge display?
I have this:
And I made this in photoshop for what I want:

While I don't think there is a way to style all the circles to show their group outline, you can achieve the effect you want by creating a union of all the circle geometries and applying your style to that. Unfortunately, Leaflet's L.circle class offers no way to access a circle's geometry beyond the center point, and to perform a union, you need the path of the circle itself. Fortunately, there is Leaflet Geodesy and its LGeo.circle class, which produces circular polygons with a given radius and number of segments. Once you have these polygon representations of your circles, you can use turf.union to produce the outline you want.
Say you are starting with a layer of points called pointLayer (this can be a L.geoJson, L.mapbox.featureLayer, or any other class that inherits the .eachLayer method). You can then iterate over the features, creating a circular polygon for each of them and adding it to a temporary layer group, like this:
var circleLayer = L.layerGroup();
var radius = 5000
var opts = {
parts: 144
};
pointLayer.eachLayer(function(layer) {
LGeo.circle(layer.getLatLng(), radius, opts).addTo(circleLayer);
});
where radius is in meters and the parts option is the number of segments you want your polygons to have. Next, use the .getLayers method to get an array of all the layers in the temporary group, then iterate over that to create a union of all the features:
var circleUnion = unify(circleLayer.getLayers()).addTo(map);
function unify(polyList) {
for (var i = 0; i < polyList.length; ++i) {
if (i == 0) {
var unionTemp = polyList[i].toGeoJSON();
} else {
unionTemp = turf.union(unionTemp, polyList[i].toGeoJSON());
}
}
return L.geoJson(unionTemp, {style: unionStyle});
}
where unionStyle is whatever style you want to apply to your newly-combined circles. Here is an example fiddle showing all this with some random data:
http://fiddle.jshell.net/nathansnider/L2d626hn/

Related

How to get the edge of a polygon drawn on map within 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

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

Leaflet - draw polyline vertices only

The title is quite clear, I'm using Leaflet and I need to show only the vertices of a polyline. For exemple see this image :
Currently I can only have the black line, I'd like only the red squares. Using markers is not an option for performance issue, my lines can be huge (500 000 vertices) and the use of smoothFactor is a need.
Is that possible? If not, does someone knows a plugin that does that, or have a hint on how could I do that by extending the Polyline class?
What you could do here is every time the polyline gets rendered, get the segments of it's SVG path, use those points to add SVG rectangle elements to the polyline's container:
var polyline = L.Polyline([]).addTo(map),
list = polyline._path.pathSegList
// Iterate segments
for (var i = 0; i < list.length; i++) {
// Create SVG rectangle element
rectangle = document.createElementNS('http://www.w3.org/2000/svg', 'rect')
// Set rectangle size attributes
rectangle.setAttributeNS(null, 'height', 4)
rectangle.setAttributeNS(null, 'width', 4)
// Set position attributes, compensate for size
rectangle.setAttributeNS(null, 'x', list[i].x - 2)
rectangle.setAttributeNS(null, 'y', list[i].y - 2)
// Set rectangle color
rectangle.setAttributeNS(null, 'fill', 'red')
// Append rectangle to polyline container
polyline._container.appendChild(rectangle)
}
Seems to work as far as i had time to test it ;) Had to use a timeout though, don't know why, look in to that when i've got more time on my hands.
Example on Plunker: http://embed.plnkr.co/vZI7aC/preview

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.