Skip to content

Commit

Permalink
Better handling of rings and holes not touched by the intersection
Browse files Browse the repository at this point in the history
  • Loading branch information
rowanwins committed Jul 6, 2022
1 parent 429f3fa commit 243311a
Show file tree
Hide file tree
Showing 11 changed files with 545 additions and 8 deletions.
7 changes: 7 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
# 0.0.11
- Fix up some handling for those rings (outer and holes) that aren't intersected by the split line.
- Add output geojson validation for test files

# 0.0.10
- Fix up packaging of dependencies

# 0.0.9
- Fix another infinite loop case with holes back to outer ring

Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "polygon-splitter",
"version": "0.0.9",
"version": "0.0.11",
"description": "Split a polygon using a polyline.",
"main": "dist/polygonsplitter.js",
"module": "dist/polygonsplitter.mjs",
Expand Down Expand Up @@ -31,7 +31,7 @@
"@rollup/plugin-commonjs": "^21.0.1",
"@rollup/plugin-node-resolve": "^13.0.6",
"@rollup/pluginutils": "^4.1.2",
"@turf/rewind": "^6.5.0",
"@placemarkio/check-geojson": "^0.1.12",
"@turf/turf": "^6.5.0",
"@vitejs/plugin-vue": "^1.9.3",
"ava": "^3.15.0",
Expand All @@ -48,7 +48,9 @@
"write-json-file": "^4.3.0"
},
"dependencies": {
"@turf/rewind": "^6.5.0",
"glob": "^8.0.3",
"point-in-polygon-hao": "^1.1.0",
"robust-predicates": "^2.0.4"
},
"keywords": [
Expand Down
8 changes: 8 additions & 0 deletions src/Contour.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
export class Contour {

constructor(contourId, coords) {
this.id = contourId
this.rawCoords = coords
}

}
3 changes: 2 additions & 1 deletion src/Edge.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
export class Edge {

constructor(p1, p2, edgeType, index) {
constructor(p1, p2, edgeType, index, contourId) {
this.p1 = p1
this.p2 = p2
this.edgeType = edgeType
this.originalIndex = index

this.polygonContourId = contourId
this.interiorRing = false

this.minX = Math.min(p1.p[0], p2.p[0])
Expand Down
12 changes: 10 additions & 2 deletions src/fillQueue.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import {Edge} from './Edge'
import {Point} from './Point'
import {Contour} from './Contour'

export function fillQueue(polygon, line, polyEdges, lineEdges, polylineBbox) {
let numberOfRingsInPolygon = 0
const contours = []

const linegeom = line.type === 'Feature' ? line.geometry : line
const linecoords = linegeom.type === 'LineString' ? [linegeom.coordinates] : linegeom.coordinates
Expand All @@ -19,7 +22,7 @@ export function fillQueue(polygon, line, polyEdges, lineEdges, polylineBbox) {
p2 = new Point(linecoords[i][ii + 1])
p1.nextPoint = p2
p2.prevPoint = p1
const e = new Edge(p1, p2, 'polyline', edgeCount)
const e = new Edge(p1, p2, 'polyline', edgeCount, null)
lineEdges.push(e)
prevEdge.nextEdge = e
e.prevEdge = prevEdge
Expand Down Expand Up @@ -48,8 +51,12 @@ export function fillQueue(polygon, line, polyEdges, lineEdges, polylineBbox) {
let polyLenth2 = polycoords[i].length

for (let ii = 0; ii < polyLenth2; ii++) {
numberOfRingsInPolygon = numberOfRingsInPolygon + 1

let polygonSet = polycoords[i][ii]
let polyLenth3 = polygonSet.length

contours.push(new Contour(numberOfRingsInPolygon, polygonSet))

const firstPoint = new Point(polygonSet[0])
let p1 = firstPoint
Expand All @@ -62,7 +69,7 @@ export function fillQueue(polygon, line, polyEdges, lineEdges, polylineBbox) {
p1.nextPoint = p2
p2.prevPoint = p1

e = new Edge(p1, p2, 'polygon', edgeCount)
e = new Edge(p1, p2, 'polygon', edgeCount, numberOfRingsInPolygon)
prevEdge.nextEdge = e
e.prevEdge = prevEdge
if (iii === 1) firstEdge = e
Expand All @@ -82,6 +89,7 @@ export function fillQueue(polygon, line, polyEdges, lineEdges, polylineBbox) {
firstPoint.prevPoint = p2.prevPoint
}
}
return contours
}

function edgeIntersectsBbox(edge, bbox) {
Expand Down
52 changes: 50 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import rewind from '@turf/rewind'
import inside from 'point-in-polygon-hao'
import {fillQueue} from './fillQueue'
import {findIntersectionPoints} from './findIntersections.js'
// import { _debugCandidatePoly, _debugIntersectionPoint, _debugLinePoints, _debugIntersectionPoints, _debugPolyStart } from './debug'
Expand All @@ -11,14 +12,25 @@ export default function (polygon, line) {
const polylineEdges = []
const polylineBbox = [Infinity, Infinity, Infinity, Infinity]

fillQueue(poly, line, polygonEdges, polylineEdges, polylineBbox)
const contours = fillQueue(poly, line, polygonEdges, polylineEdges, polylineBbox)

findIntersectionPoints(polygonEdges, polylineEdges, intersections)

if (intersections.length === 0) {
return polygon
}

// Track the number of intersections per contour
// This is useful for holes or outerrings that aren't intersected
// so that we can manually add them back in at the end
const numberIntersectionsByRing = {}
contours.forEach(c => numberIntersectionsByRing[c.id] = 0) //eslint-disable-line
intersections.forEach(i => {
const id = i.polygonEdge.polygonContourId
numberIntersectionsByRing[id] = numberIntersectionsByRing[id] + 1
})


let infiniteLoopGuard = 0
const outPolys = []
// _debugIntersectionPoints(intersections)
Expand Down Expand Up @@ -119,15 +131,51 @@ export default function (polygon, line) {
polyStart = nextPolyStart
}

const outCoordinates = outPolys.map(poly => [poly])

const keys = Object.keys(numberIntersectionsByRing)
for (let index = 0; index < keys.length; index++) {
const key = keys[index]
const value = numberIntersectionsByRing[key]
if (value === 0) {
const edge = findFirstPolygonEdge(polygonEdges, parseInt(key))
const ring = findRingFromEdge(edge, contours)
createAsHoleOrAddAsNewOuterRing(ring, outCoordinates)
}
}

return {
type: 'Feature',
properties: {},
geometry: {
type: 'MultiPolygon',
coordinates: outPolys.map(poly => [poly])
coordinates: outCoordinates
}
}
}

function findFirstPolygonEdge(polygonEdges, contourId) {
for (let index = 0; index < polygonEdges.length; index++) {
const edge = polygonEdges[index];
if (edge.polygonContourId === contourId) return edge
}
}

function findRingFromEdge(edge, contours) {
const contour = contours.find(c => c.id === edge.polygonContourId)
return contour.rawCoords
}

function createAsHoleOrAddAsNewOuterRing(unusedRing, outCoordinates) {
for (let index = 0; index < outCoordinates.length; index++) {
const existingRing = outCoordinates[index];
if (inside(unusedRing[0], [existingRing[0]])) {
existingRing.push(unusedRing)
return
}
}
// If no match is found push it as a new outer ring
outCoordinates.push([unusedRing])
}

// Walk around the polygon collecting vertices
Expand Down
105 changes: 105 additions & 0 deletions test/harness/in/multi-with-hole.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"expected": 2
},
"geometry": {
"type": "MultiPolygon",
"coordinates": [
[
[
[
-125.827487,
44.831595
],
[
-128.945652,
42.673378
],
[
-125.459447,
38.967804
],
[
-117.461429,
38.466534
],
[
-121.17349,
44.386244
],
[
-125.827487,
44.831595
]
],
[
[
-124.41171,
41.47705
],
[
-126.605215,
42.74795
],
[
-124.642412,
43.318244
],
[
-123.576387,
42.246982
],
[
-124.41171,
41.47705
]
],
[
[
-121.385261,
39.899593
],
[
-119.430181,
40.946759
],
[
-122.039683,
42.383191
],
[
-121.385261,
39.899593
]
]
]
]
}
},
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "LineString",
"coordinates": [
[
-123.478021,
46.254241
],
[
-123.169157,
42.701512
],
[
-123.834531,
37.504321
]
]
}
}
]
}
Loading

0 comments on commit 243311a

Please sign in to comment.