Leaflet-geoman how to properly retrieve GeoJSON - javascript

I'm having some troubles when it comes to using the draw control given by leaflet-geoman:
What I'm trying to achieve:
Draw Polygon(s) --> ✅
Get geojson from layer(s) --> ✅
Store in database --> ✅
Retrieve from database --> ✅
Load in leaflet map and be able to edit, crop it and remove it. --> ✅ ?
Get new geojson from modified layer(s) or if I had 2 layers and removed one, get only one. --> X
I haven't been able to get the new geojson from the modifications I did through the geoman draw control. No matter what I do, I always get the same result I had in the beginning when I loaded the data to the map.
Fiddle: https://jsfiddle.net/pjkLr41q/34/
const shapes = [{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-3.701856, 40.422481],
[-3.707092, 40.418593],
[-3.70177, 40.417809],
[-3.701899, 40.422873],
[-3.701856, 40.422481]
]
]
}
}];
const geojson = L.geoJSON(shapes).addTo(map);
map.eachLayer((layer) => {
if (layer.pm) {
const geojson = layer.toGeoJSON();
if (layer instanceof L.Circle) {
geojson.properties.radius = 10;
}
shapes.push(geojson);
}});
I ain't sure if its because the way I load the data, straight with the L.geoJSON(data) or maybe going through the eachLayer function isn't what I need in this case, but I'm kinda lost right now. Help really appreciated.

You can get all Geoman layers with: L.PM.Utils.findLayers(map).
In the next Version 2.7.0 there will be the functions map.pm.getGeomanLayers() and map.pm.getGeomanDrawLayers()
So you can get the new geojson with:
var layers = L.PM.Utils.findLayers(map);
var group = L.featureGroup();
layers.forEach((layer)=>{
group.addLayer(layer);
});
shapes = group.toGeoJSON();

Related

OpenStreetMap vanishes and popups are visible in wrong geolocations [duplicate]

Salutations all and happy holidays.
I Noticed an interesting behavioral quirk while trying to draw polygon layers with L.geoJson(). consider the following code:
var polygonCoords = [
{"type": "Feature",
"properties": {"group": "Violations"},
"geometry": {
"type" : "Polygon",
"coordinates": [[
[-107.69348, 43.22519],
[-105.48523, 42.99259],
[-107.7594, 42.26105]
]]
}
}];
and
var polygons = L.polygon([
[43.22519, -107.69348],
[42.99259, -105.48523],
[42.26105, -107.7594]
]);
Now, both work in their respective contexts. I was just wondering why the coordinate matrix within L.polygon() has to be reflected in order to show up where one expects it to be when passed into L.goeJson() like so:
var jsonPoly = L.geoJson(polygonCoords, {
style: function(feature) {
if (feature.properties.group == "Violations") {
return {color: "#ff0000"};
}
}
});
Or is this an oversight within leaflet? Also, is there a way to automate this reflection with say toGeoJson(polygons)?
Thanks so much all.
When creating a geoJson layer the coordinates are expected to match the GeoJSON standard (x,y,z or lng, lat, altitude) (GeoJSON position specs)
If you have string of GeoJSON where your coordinates are not in this format, you can create your GeoJSON layer with a custom coordsToLatLng function that will handle this conversion to the standard's format (Leaflet Doc)
If you have a polygon layer and want to add it to an existing GeoJSON feature group you can do something like:
var polygons = L.polygon([
[43.22519, -107.69348],
[42.99259, -105.48523],
[42.26105, -107.7594]
]);
var gg = polygons.toGeoJSON();
var jsonFeatureGroup = L.geoJson().addTo(map);
jsonFeatureGroup.addData(gg);
map.fitBounds(jsonFeatureGroup.getBounds());

Hide leaflet features out of specified area

I'm trying to hide some leaflet features outside of a defined area.
I have a leaflet map displaying rivers as features on a RiverLayer and a circleLayer used to draw an area around the current center of the map.
Each river is separated in multiple parts inside my database and I retrieve only the parts intersecting with my current circle area.
The result look like this:
The rivers are showing outside the area, because I selected the parts intersecting with it.
I could select in my database all the parts within the area but I would lose the parts that are not entirely inside the area.
Calculating the intersection point for each part concerned in order to adjust the coordinates would be a solution but a complex one.
In fact, I would prefer to simply hide these overflows on the client side but I can't find a solution.
Is there a possibility with leaflet to do something like this?
Thanks for you time
Here is an example using turfJS using the booleanWithin and lineSplit functions.
I did the example on a simple basic HTML and Vanilla JS. I added another linestring on the "river" to simulate an outside the circle river
var mymap = L.map('mapid').setView([43.63458105967136, 1.1613321304321291], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 20,
attribution: 'Map data © OpenStreetMap contributors',
}).addTo(mymap);
var center = [43.63458105967136, 1.1613321304321291];
var radius = 1500;
// L.circle(center, radius, {
// color: '#ff4081', fillColor: '#ff4081', fillOpacity: 0.5
// }).addTo(mymap);
var riverGeoJSON = [
{ "type": "Feature", "geometry": { "coordinates": [[1.159444487444759, 43.633815447205706], [1.160243520516838, 43.634633600388156], [1.160731009187281, 43.6350432633719], [1.161774921971743, 43.63541373375439], [1.162079879908259, 43.63564209781788], [1.162320030539753, 43.635959368371424], [1.162373764624914, 43.636409391647234], [1.161800286153361, 43.637212422659154], [1.160910734693605, 43.63832601539633], [1.160651867030764, 43.63886255455486], [1.160332394101095, 43.639317964879666], [1.159189872203288, 43.640743176542664], [1.158053840843969, 43.641810274789506], [1.156922548158863, 43.642651534145514], [1.155851918485514, 43.64349381183714], [1.155156982509935, 43.644214650781954], [1.15326441791592, 43.64594659208024], [1.152374775964331, 43.6470151231795], [1.151428904349222, 43.64790448439313], [1.151107886218696, 43.64840394819371]], "type": "LineString" } },
{ "type": "Feature", "geometry": { "coordinates": [[1.156570800342349, 43.632121495293006], [1.158291185472127, 43.63272397754135], [1.158901458643683, 43.633090727638866], [1.159444487444759, 43.633815447205706]], "type": "LineString" } },
{ "type": "Feature", "geometry": { "coordinates": [[1.168152938761366, 43.62917262321181], [1.167467920251437, 43.62939958202886], [1.166101976396903, 43.62960874939632], [1.164673843635074, 43.629863651007135], [1.163738326615552, 43.63021236020524], [1.163236303364402, 43.630566588076604], [1.162728104605807, 43.63119071739829], [1.161282685092185, 43.632253508072225], [1.160336935333006, 43.633151033736986], [1.159444487444759, 43.633815447205706]], "type": "LineString" } },
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[
1.0526275634765625,
43.550289946081115
],
[
1.07940673828125,
43.63334186269
],
[
1.0764884948730469,
43.6336524704596
]
]
}
}
];
// L.geoJSON(riverGeoJSON, {}).addTo(mymap);
var centerGeoJSON = [center[1], center[0]];
var radiusGeoJSON = radius / 1000;
var options = { steps: 50, units: 'kilometers' };
var circleGeoJSON = turf.circle(centerGeoJSON, radiusGeoJSON, options);
L.geoJSON(circleGeoJSON, {}).addTo(mymap);
var riverClipped = {}
for (let index = 0; index < riverGeoJSON.length; index++) {
const feature = riverGeoJSON[index];
var within = turf.booleanWithin(feature, circleGeoJSON);
console.log({ within });
var split = turf.lineSplit(feature, circleGeoJSON);
console.log({ split });
if (within && split.features.length === 0) {
L.geoJSON(feature, {}).addTo(mymap);
} else {
L.geoJSON(split.features[0], {}).addTo(mymap);
}
}
Circle is calculated with turfJS to have a valid GeoJSON feature. This feature is then used as a splitter.
When line is completely inside the circle, the within function returns true, the split function doesn't return a split feature.
When line is completely outside the circle, the within function is false and the split function doesn't return a split feature.
When the line intersect the circle, the within function returns false, and the first feature from the split feature collection is the one inside the circle.
Complete source code on JSFiddle: https://jsfiddle.net/tsamaya/6sc58m7u/

How to retrieve individal latlng object with getGeometry()?

I have a .geojson file. Inside the file there is one Feature and that feature has this property:
"geometry": { "type": "LineString", "coordinates": [ [ 103.766547, 1.377559, 0.0 ], [ 103.771405, 1.374479, 0.0 ] ] }
What I am trying to do now is something like this: when I mouse over a feature, I want to get two separate latlng objects from within the coordinates array. Using my code below:
map.data.addListener("mouseover", function(event) {
var coordinates = event.feature.getGeometry("coordinates");
console.log(coordinates);
});
Below is what is shown inside the console:
so the coordinates variable contains one 'i' property. 'i' is an array with two objects inside it.
Is it possible to get two separate latlng object from this array ?
I want to achieve this because I hope that later I can get the distance between these two latlng objects.
LineString has a .getArray() method which returns the array of LatLng objects:
map.data.addListener("mouseover", function(event) {
var coordinates = event.feature.getGeometry("coordinates").getArray();
for (var i=0; i<coordinates.length; i++) {
console.log(coordinates[i].toUrlValue(6));
}
});
proof of concept fiddle

Openlayers 2, specifying DOM Id for a Geometry/Feature?

Is there any way I can specify/set the DOM Id of an OpenLayers Geometry Point object i.e. call it "myID" instead of "OpenLayers_Geometry_Point_nnn"?
As far as I can tell, in Openlayers 2.13, creating a OpenLayers.Geometry.Point object does not accept any form of DOM id attribute, and creates its own unique ID through `OpenLayers.Util.createUniqueID. The OpenLayers.Geometry.Point.initialize 'constructor' only accepts X and Y values, not additional info.
I'd really like to use Selenium to verify/manipulate certain objects are on a map, and having predictable DOM Ids seems the be best way to do this.
One "solution" I have implemented is to replace OpenLayers.Util.createUniqueID whilst I am creating the objects I wish to test.
// replace OpenLayer Dom ID generation for this layer
var olPrefix = "MyPrefix";
var olCount = 0;
var old = OpenLayers.Util.createUniqueID;
OpenLayers.Util.createUniqueID = function(prefix) {
if (prefix.search( '^OpenLayers.Geometry') >= 0) {
// special Dom IDs for Geometry nodes only...
olCount++;
return olPrefix + "_" + olCount;
}
// default to using the previous ID generator...
return( old( prefix));
}
var geoJSON = {
"type": "FeatureCollection",
"features": [
{
"type":"Feature",
"geometry": { "type":"Point","coordinates":[ 1.0, 52.0]},
"properties":{ "myProperty":"myValue" }
},
// more features here
]
};
var geoformat = new OpenLayers.Format.GeoJSON();
var features = geoformat.read( geoJSON);
// finished creating your features, so put the "old" ID routine back...
OpenLayers.Util.createUniqueID = old;
Another possibility is to replace OpenLayers.Geometry.initialize in some way

Removing old markers and Polylines before adding new ones - Mapbox

Given the following code for Mapbox, whereby I am plotting points and polylines between a number of points. User will choose a different selection and the results of that will replace the pins on the map. These pins will then be drawn together in the correct order using polylines.
$.post('_posts/get-pins.php', {traveller: $(this).val()}, function(data){
var featureLayer = L.mapbox.featureLayer().addTo(map);
var featureCollection = {
"type": "FeatureCollection",
"features": []
};
var lineArray = [];
$.each(data, function (k, item) {
featureCollection.features.push({
"type": "Feature",
"properties": {
"id": item.id,
"title": item.title,
"description": item.description,
"image": item.image,
"marker-symbol": "star",
"marker-color": "#ff8888",
"marker-size": "large"
},
"geometry": {
"type": "Point",
"coordinates": [
item.long,
item.lat
]
}
});
lineArray[item.id] = [item.lat, item.long];
});
featureLayer.setGeoJSON(featureCollection);
lineArray = lineArray.filter(function(){return true});
var polyline = L.polyline(lineArray).addTo(map);
},'json');
So I need to remove the polylines and the markers before plotting the new ones. I have tried numerous combinations of map.removeLayer(xxx) replacing xxx with many of the variables that are being created but all I have managed to do is remove the markers. It just leaves the polylines intact and just stacks the polyline layers.
Declare your variables for the featureLayer and polyline outside of the method/function you are using to update them:
var featureLayer, polyline;
In the function, check if the variable featureLayer already is an instance of FeatureLayer then clear it's layers, if it's not create the new layer:
if (featureLayer instanceof L.mapbox.FeatureLayer) {
featureLayer.clearLayers();
} else {
featureLayer = L.mapbox.featureLayer().addTo(map);
}
With the polyline you got to do it differently because it hasn't got a function to clear al the added points, just check if it's an instance of L.Polyline, if so remove it from the map using L.Map's removeLayer method and afterwards just define a new polyline:
if (polyline instanceof L.Polyline) {
map.removeLayer(polyline);
}
polyline = L.polyline([]).addTo(map);
Working example on Plunker: http://plnkr.co/edit/7nlgiA50NuPGsQOF0Fv4?p=preview

Categories