Skip to content

Instantly share code, notes, and snippets.

@simlu
Created April 10, 2021 05:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save simlu/f278964aa8c4420622062b6a19bd8941 to your computer and use it in GitHub Desktop.
Save simlu/f278964aa8c4420622062b6a19bd8941 to your computer and use it in GitHub Desktop.
Merge polygons and exclude holeless, self-intersecting polygons
const assert = require('assert');
const polygon = require('turf-polygon');
const point = require('turf-point');
const { default: distance } = require('@turf/distance');
const { default: union } = require('@turf/union');
const difference = require('@turf/difference');
const merge = (polygons) => polygons.reduce((p, c) => union(p, polygon([c])), polygon([]));
const normalize = (poly, ...holes) => [holes.reduce((p, h) => {
let dist = Number.MAX_VALUE;
let closest = [0, 0];
for (let x = 0; x < p.length - 1; x += 1) {
const ptX = point(p[x]);
for (let y = 0; y < h.length - 1; y += 1) {
const distNew = distance(ptX, point(h[y]));
if (dist > distNew) {
closest = [x, y];
dist = distNew;
}
}
}
const r = [
...p.slice(closest[0], -1),
...p.slice(0, closest[0] + 1),
...h.slice(closest[1]),
...h.slice(1, closest[1] + 1),
p[closest[0]]
];
return r;
}, poly)];
const combine = (include, exclude) => {
const plus = merge(include);
const minus = merge(exclude);
const { geometry } = difference(plus, minus);
if (geometry.type === 'MultiPolygon') {
return geometry.coordinates.map((poly) => normalize(...poly));
}
assert(geometry.type === 'Polygon');
return [normalize(...geometry.coordinates)];
};
const coordinates = combine([
[[-3.0322265625, 25.58208527870072], [0.9667968749999999, 25.58208527870072], [0.9667968749999999, 28.05259082333983], [-3.0322265625, 28.05259082333983], [-3.0322265625, 25.58208527870072]],
[[-4.130859375, 12.897489183755892], [13.271484375, 12.897489183755892], [13.271484375, 22.917922936146045], [-4.130859375, 22.917922936146045], [-4.130859375, 12.897489183755892]],
[[8.0859375, 21.289374355860424], [16.083984375, 21.289374355860424], [16.083984375, 26.667095801104814], [8.0859375, 26.667095801104814], [8.0859375, 21.289374355860424]]
], [
[[-8.876953125, 19.228176737766262], [-0.17578125, 19.228176737766262], [-0.17578125, 26.115985925333536], [-8.876953125, 26.115985925333536], [-8.876953125, 19.228176737766262]],
[[2.197265625, 15.876809064146757], [5.9765625, 15.876809064146757], [5.9765625, 18.729501999072138], [2.197265625, 18.729501999072138], [2.197265625, 15.876809064146757]],
[[6.328125, 19.9526963975442], [8.1298828125, 19.9526963975442], [8.1298828125, 21.49396356306447], [6.328125, 21.49396356306447], [6.328125, 19.9526963975442]],
[[7.84423828125, 16.636191878397664], [9.31640625, 16.636191878397664], [9.31640625, 18.020527657852337], [7.84423828125, 18.020527657852337], [7.84423828125, 16.636191878397664]],
[[0.54931640625, 18.60460138845525], [1.494140625, 18.60460138845525], [1.494140625, 19.37334071336406], [0.54931640625, 19.37334071336406], [0.54931640625, 18.60460138845525]],
[[2.70263671875, 19.456233596018], [3.8671874999999996, 19.456233596018], [3.8671874999999996, 20.571081893508193], [2.70263671875, 20.571081893508193], [2.70263671875, 19.456233596018]],
[[4.482421875, 20.612219573881042], [5.4052734375, 20.612219573881042], [5.4052734375, 21.350781150679737], [4.482421875, 21.350781150679737], [4.482421875, 20.612219573881042]],
[[11.074218749999998, 13.624633438236152], [12.50244140625, 13.624633438236152], [12.50244140625, 14.753635331540442], [11.074218749999998, 14.753635331540442], [11.074218749999998, 13.624633438236152]]
]);
console.log(JSON.stringify({
type: 'Feature',
properties: {},
geometry: { type: 'MultiPolygon', coordinates }
}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment