# 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 = '&copy; <a href="http://openstreetmap.org/copyright">OpenStreetMap</a> 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 });

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;

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' };
}
layers.push(layer);
};
var lowerCut = cutPolygon(polygon, line, -1, 'lower');
if (lowerCut != null) {
layer = L.geoJSON(lowerCut, {
style: function(feature) {
return {color: 'yellow' };
}
layers.push(layer);
};
});
};
});
``````

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

I got this from this here • Welcome to GIS SE! We're a little different from other sites. We're a Q&A site, not a discussion forum. For questions that involve code we ask that you show us where you are stuck with your own code by including a code snippet in your question. There is an edit button beneath your question which will enable you to do that and a `{}` button that enables you to format any highlighted code nicely. Please check out our short tour for more about how the site works. Thanks. – PolyGeo Oct 10 '19 at 9:07

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);
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, {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);
}
else if (geom.type == 'LineString') {
var line = geom;
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' };
}
layer = L.geoJSON(lowerCut, {
style: function(feature) {
return {color: 'yellow' };
}
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' };
}
}
});
polygons = newPolygons;
};
});
``````

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

I have managed to split a polygon by multiple linestrings with a little tweaking. Here is the Code.

Rendering the split polygons.

``````this.map.on('draw:created', (event) => {
this.multiSelectByclick = false;
if (this.selectedParcelId && event.layerType === 'polyline') {
let layer = event.layer;

let geojson = layer.toGeoJSON();
let geom = turf.getGeom(geojson);
this.curPly = turf.getGeom(this.samplePoly);
console.log(geom.type, 'type', this.samplePoly);
// if (geom.type === 'Polygon')
//   this.samplePoly.push(geom);

if (geom.type === 'LineString') {
let line = geom;
this.polyLineString.push(line);

this.layers.forEach((layer, index) => {
layer.remove();
});
console.log(line, 'line split - from new')
};
}
});
``````

Splitting the Polygons

``````splitParcels() {
const parcelsArray = [];
parcelsArray.push({ localId: UUID.UUID(), polygon: this.curPly });
console.log('parcelsArray: ', parcelsArray);
console.log('polyLineString: ', this.polyLineString);
this.polyLineString.forEach(line => {
const resultsArray = [];
parcelsArray.forEach(polygon => {
console.log('polygon: ', polygon);
const result = this.splitPolygon(polygon, line);
console.log('result: ', result);
resultsArray.push(result);
});
console.log('resultsArray: ', resultsArray);
resultsArray
.filter(result => result.upperChild && result.lowerChild)
.forEach(result => {
const index = parcelsArray.findIndex(parcel => parcel.localId === result.parent.localId);
parcelsArray.splice(index, 1, result.upperChild);
parcelsArray.splice(index, 0, result.lowerChild);
});
});
console.log('parcelsArray: ', parcelsArray);
const splitPolygons = parcelsArray.map(parcel => parcel.polygon);
console.log('splitPolygons: ', splitPolygons);
this.showSplitedPolygons(splitPolygons);
return splitPolygons;

}

showSplitedPolygons(polygons) {
let shows = [polygons]
if (shows) {
let style;
this.updateGeomLyr.clearLayers();
shows.forEach(element => {
console.log(element, 'sshowSplitedPolygons')
element.map(el => {
new L.Polygon(this.mapService.invertCoordinatesArray(el.coordinates),
style = {
fillColor: 'green'
},
})
});
}
}

splitPolygon(polygon, line) {
console.log('In Split Polygon');
console.log('polygon: ', polygon);
const result = {
parent: polygon, upperChild: undefined, lowerChild: undefined
};
const upperPolygon = this.cutPolygon(polygon.polygon, line, 1, 'upper');
console.log('upperPolygon: ', upperPolygon);
const lowerPolygon = this.cutPolygon(polygon.polygon, line, -1, 'lower');
console.log('lowerPolygon: ', lowerPolygon);
if (upperPolygon && lowerPolygon) {
result.lowerChild = { localId: UUID.UUID(), polygon: lowerPolygon };
result.upperChild = { localId: UUID.UUID(), polygon: upperPolygon };
}
console.log('result: ', result);
return result;
}

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

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

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

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

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

for (j = 0; j < clipped.geometry.coordinates.length; j++) {
let polyg = turf.polygon(clipped.geometry.coordinates[j]);
let 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 turf.getGeom(retVal);
}
``````

Before splitting After Splitting • This supports only linestrings with two points, not more – Arul Rozario Oct 11 '19 at 14:04
• If someone has a solution to support linestrings with multiple points that would be great. – Arul Rozario Oct 11 '19 at 14:05