buildTurfPolygon = {
function splitRing(ring) {
const splitpoints = createPointHashSet(ring.length);
{
const allPoints = createPointHashSet(ring.length);
for (let coord of ring.slice(0, -1)) {
if (allPoints.has(coord)) splitpoints.add(coord);
allPoints.add(coord);
}
}
if (splitpoints.values().length == 0) return [ring];
splitpoints.add(ring[0]);
let segment = [];
const segments = [segment];
const ringSegments = [];
const lastIndex = ring.length - 1;
for (let i = 0; i < ring.length; i++) {
const coord = ring[i];
segment.push(coord);
if (splitpoints.has(coord)) {
if (segment.length > 1) {
const ringStartIdx = segments.findLastIndex(s => pointsEqual(s[0], coord));
if (ringStartIdx >= 0) ringSegments.push(segments.splice(ringStartIdx));
}
if (i == 0 || i == lastIndex) continue;
segment = [coord];
segments.push(segment);
}
}
return ringSegments.map(rs => [rs[0][0], ...rs.flatMap(s => s.slice(1))]);
}
function buildTurfPolygon(rings, options) {
options = Object.assign({ ignoreHoles: false }, options);
const { ignoreHoles } = options;
const ringMeta = rings.flatMap(r => splitRing(r))
.map((ring,i) => {
const lineString = turf.lineString(ring);
turf.cleanCoords(lineString, { mutate: true });
const group = turf.booleanClockwise(lineString) ? "holeMeta" : "shellMeta";
if (ignoreHoles && group == "holeMeta") return undefined;
const polygon = turf.lineToPolygon(lineString);
polygon.properties = { index: i, key: "potato" };
return {
ring: turf.getCoords(lineString),
group,
bbox: turf.bbox(polygon),
area: Math.abs(d3.polygonArea(ring)),
polygon
};
})
.filter(rm => rm);
ringMeta.sort((a,b) => a.area - b.area);
let { shellMeta, holeMeta } = _.groupBy(ringMeta, rm => rm.group);
shellMeta ??= [];
holeMeta ??= [];
shellMeta.forEach(shell => shell.holes = []);
if (!ignoreHoles) {
for (let hole of holeMeta) {
let shell;
if (shellMeta.length == 1) {
shell = shellMeta[0];
}
else {
const candidates = shellMeta.filter(shell => shell.area > hole.area && Bbox.covers(shell.bbox, hole.bbox));
if (candidates.length == 1) {
shell = candidates[0];
}
else {
shell = candidates.find(shell => turf.booleanCovers(shell.polygon, hole.polygon));
console.log(`ran booleanCovers ${candidates.indexOf(shell) + 1} times`);
}
}
if (!shell) {
debugger;
throw "couldn't find shell for hole";
}
shell.polygon.geometry.coordinates.push(hole.ring);
}
}
const shells = shellMeta.map(s => s.polygon);
const fcPolygons = turf.dissolve(turf.featureCollection(shells))
const fcMultiPolygon = turf.combine(fcPolygons);
if (fcMultiPolygon.features.length != 1) {
debugger;
throw "Got multiple multipolygons";
}
const result = fcMultiPolygon.features[0];
result.properties = {};
return result;
}
return buildTurfPolygon;
}