Sunday, 15 April 2018

Splitting A polygon into multiple polygon by multiple line strings in Leaflet and turf.js


I have need to split a polygon into multiple polygons based on the line strings drawn on the polygon. I have come across another post that splits a polygon into two based one line string. If a new line is drawn then the existing lines are ignored and the polygon is split into two based on the new line. What I want to achieve is 'split a polygon into multiple polygons based on multiple line.


 var osmUrl = 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
var osmAttrib = '© OpenStreetMap contributors';
var osm = L.tileLayer(osmUrl, { maxZoom: 18, attribution: osmAttrib });

var drawnItems = L.featureGroup();

var map = new L.Map('map', { center: new L.LatLng(51.505, -0.04), zoom: 13 });

osm.addTo(map);
drawnItems.addTo(map);

map.addControl(new L.Control.Draw({
draw: {
marker: false,

circle: false,
circlemarker: false,
rectangle: false,
polygon: {
allowIntersection: false,
showArea: true
}
}
}));


function cutPolygon(polygon, line, direction, id) {
var i = -1;
var j;
var polyCoords = [];
var retVal = null;

if ((polygon.type != 'Polygon') || (line.type != 'LineString')) return retVal;
if (line.coordinates.length != 2) return retVal;

var intersectPoints = turf.lineIntersect(polygon, line);

var nPoints = intersectPoints.features.length;
if ((nPoints == 0) || ((nPoints % 2) != 0)) return retVal;

var offsetLine = turf.lineOffset(line, (0.01 * direction), {units: 'kilometers'});
var thickLineCorners = turf.featureCollection([line, offsetLine]);
var thickLinePolygon = turf.convex(turf.explode(thickLineCorners));

var clipped = turf.difference(polygon, thickLinePolygon);

for (j = 0; j < clipped.geometry.coordinates.length; j++) {

var polyg = turf.polygon(clipped.geometry.coordinates[j]);
var overlap = turf.lineOverlap(polyg, line, {tolerance: 0.005});
if (overlap.features.length > 0) {
polyCoords[++i] = turf.coordAll(polyg);
};
};

if (i == 0)
retVal = turf.polygon(polyCoords, {id: id});
else if (i > 0) {

retVal = turf.multiPolygon([polyCoords], {id: id});
}

return retVal;
};

var polygons = [];
var layers = [];

map.on(L.Draw.Event.CREATED, function (event) {

var layer = event.layer;
drawnItems.addLayer(layer);

var geojson = layer.toGeoJSON();
var geom = turf.getGeom(geojson);

if (geom.type == 'Polygon')
polygons.push(geom);
else if (geom.type == 'LineString') {
var line = geom;

layers.forEach(function (layer, index) {
layer.remove();
});
layers = [];
polygons.forEach(function (polygon, index) {
var layer;
var upperCut = cutPolygon(polygon, line, 1, 'upper');
if (upperCut != null) {
layer = L.geoJSON(upperCut, {
style: function(feature) {

return {color: 'green' };
}
}).addTo(map);
layers.push(layer);
};
var lowerCut = cutPolygon(polygon, line, -1, 'lower');
if (lowerCut != null) {
layer = L.geoJSON(lowerCut, {
style: function(feature) {
return {color: 'yellow' };

}
}).addTo(map);
layers.push(layer);
};
});
};
});

I want the polygon split into 12 polygons. now it's just two(green & yellow).


I got this from this here



Example of what I am looking for



Answer



Update: See improved split method here: Splitting a polygon by multiple linestrings leaflet and turf.js


Code mentioned in the question was produced as kind of a proof of concept that polygons can be splitted with Turf.js library (see Client-Side Polygon split) It's not very robust and thoroughly tested.


Below is modified code which allows to split polygons multiple times with lines that have multiple points. Again it not robust and thoroughly tested, just a basic proof of concept.


At each step (split) the following layers and arrays are updated:



  • Layer drawnPolygons contains all polygons, split and unsplit

  • Layer drawnLines contains all lines used for splitting

  • Array polygons contains all polygons that correspond to drawnPolygons layer



The main part of the code:


function cutPolygon(polygon, line, direction, id) {
var j;
var polyCoords = [];
var cutPolyGeoms = [];
var retVal = null;

if ((polygon.type != 'Polygon') || (line.type != 'LineString')) return retVal;


var intersectPoints = turf.lineIntersect(polygon, line);
var nPoints = intersectPoints.features.length;
if ((nPoints == 0) || ((nPoints % 2) != 0)) return retVal;

var offsetLine = turf.lineOffset(line, (0.01 * direction), {units: 'kilometers'});

for (j = 0; j < line.coordinates.length; j++) {
polyCoords.push(line.coordinates[j]);
}
for (j = (offsetLine.geometry.coordinates.length - 1); j >= 0; j--) {

polyCoords.push(offsetLine.geometry.coordinates[j]);
}
polyCoords.push(line.coordinates[0]);
var thickLineString = turf.lineString(polyCoords);
var thickLinePolygon = turf.lineToPolygon(thickLineString);

var clipped = turf.difference(polygon, thickLinePolygon);
for (j = 0; j < clipped.geometry.coordinates.length; j++) {
var polyg = turf.polygon(clipped.geometry.coordinates[j]);
var overlap = turf.lineOverlap(polyg, line, {tolerance: 0.005});

if (overlap.features.length > 0) {
cutPolyGeoms.push(polyg.geometry.coordinates);
};
};

if (cutPolyGeoms.length == 1)
retVal = turf.polygon(cutPolyGeoms[0], {id: id});
else if (cutPolyGeoms.length > 1) {
retVal = turf.multiPolygon(cutPolyGeoms, {id: id});
}


return retVal;
};

var polygons = [];

map.on(L.Draw.Event.CREATED, function (event) {
var layer = event.layer;

var geojson = layer.toGeoJSON();

var geom = turf.getGeom(geojson);

if (geom.type == 'Polygon') {
polygons.push(geom);
drawnPolygons.addLayer(layer);
}
else if (geom.type == 'LineString') {
var line = geom;
drawnLines.addLayer(layer);
drawnPolygons.clearLayers();

var newPolygons = [];
polygons.forEach(function (polygon, index) {
var cutDone = false;
var layer;
var upperCut = cutPolygon(polygon, line, 1, 'upper');
var lowerCut = cutPolygon(polygon, line, -1, 'lower');
if ((upperCut != null) && (lowerCut != null)) {
layer = L.geoJSON(upperCut, {
style: function(feature) {
return {color: 'green' };

}
}).addTo(drawnPolygons);
layer = L.geoJSON(lowerCut, {
style: function(feature) {
return {color: 'yellow' };
}
}).addTo(drawnPolygons);
cutDone = true;
};
if (cutDone) {

newPolygons.push(upperCut.geometry);
newPolygons.push(lowerCut.geometry);
}
else {
newPolygons.push(polygon);
layer = L.geoJSON(polygon, {
style: function(feature) {
return {color: '#3388ff' };
}
}).addTo(drawnPolygons);

}
});
polygons = newPolygons;
};
});

Working JSFiddle is available at https://jsfiddle.net/TomazicM/x1a4d9ho/.


No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...