Skip to content

Instantly share code, notes, and snippets.

@mcwhittemore
Created February 19, 2021 14:37
Show Gist options
  • Save mcwhittemore/29cf8312bcb4b62b66988f87488b0392 to your computer and use it in GitHub Desktop.
Save mcwhittemore/29cf8312bcb4b62b66988f87488b0392 to your computer and use it in GitHub Desktop.
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
31.07826232910156,
-26.286643169805362
],
[
31.04598999023437,
-26.32542195842115
],
[
31.09508514404297,
-26.36018871948795
],
[
31.141433715820312,
-26.309111826816526
],
[
31.11671447753906,
-26.268480389348543
],
[
31.07826232910156,
-26.286643169805362
]
]
]
}
}
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
31.81616572197592,
-26.799702899249567
],
[
33.28212621660443,
-27.703479899656887
],
[
32.23977118957961,
-27.060859240276358
],
[
31.81616572197592,
-26.799702899249567
]
]
]
}
}
file:///Users/matthewwhittemore/crosscut/fast-catch/smash.mjs:75
throw new Error('Repeat');
^
Error: Repeat
at smash (file:///Users/matthewwhittemore/crosscut/fast-catch/smash.mjs:75:11)
at smasher (file:///Users/matthewwhittemore/crosscut/fast-catch/smash.mjs:35:7)
at file:///Users/matthewwhittemore/crosscut/fast-catch/smash.mjs:15:15
at ModuleJob.run (internal/modules/esm/module_job.js:145:37)
at async Loader.import (internal/modules/esm/loader.js:182:24)
at async Object.loadESM (internal/process/esm_loader.js:68:5)
import { readFileSync } from 'fs';
import Delaunator from 'delaunator';
// I guess you can't import json in node@12, I didn't try higher.
function require(path) {
return JSON.parse(readFileSync(path).toString());
}
const area = require('./area.json');
import turfCenter from '@turf/center-of-mass';
import turfArea from '@turf/area';
const seen = {};
// Call smasher and, if it works, print out the new polygons as a FeatureCollection
const polys = smasher(area, 100);
const str = JSON.stringify({
type: 'FeatureCollection',
features: polys.slice(0, 10)
});
console.log(str);
// Tail recursive delaunator. Takes one polygon and returns a list of them
// maxArea is in square meters
function smasher(input, maxArea) {
const polys = smash(input);
const out = [];
while(polys.length > 0) {
const poly = polys.pop();
const area = turfArea(poly);
if (area < maxArea) {
out.push(poly);
}
else {
smash(poly).forEach(p => polys.push(p));
}
}
return out;
}
// find the mid point between two points
function mid(a, b) {
const dx = a[0] - b[0];
const dy = a[1] - b[1];
return [a[0] + dx/2, a[1] + dy/2]
}
// Break a polygon up into triangles by using
// the vertices and the midpoints of the lines
// which make up the provided polygon
function smash(poly) {
const coords = poly.geometry.coordinates[0];
const points = []
// Loop over the coords but skip the last one
// to avoid duplicates in the point set
// for each coord add it to the list of points
// along with the mid point of the current coord
// and the next coord in the list
for(let i=0; i<coords.length-1; i++) {
const a = coords[i];
const b = coords[i+1];
const m = mid(a, b);
points.push(a);
points.push(m);
}
// This is here to catch the error being reported
// ideally we wouldn't need to do this check
const id = coords.join(';');
if (seen[id] === 1) {
console.error(JSON.stringify(poly, null, 2));
throw new Error('Repeat');
}
seen[id] = (seen[id] || 0) + 1;
const delaunay = Delaunator.from(points);
const tris = delaunay.triangles;
const polys = [];
// Convert triangle array into GeoJSON polygons
for (let i = 0; i < tris.length; i += 3) {
polys.push({
type: "Feature",
properties: {},
geometry: {
type: "Polygon",
coordinates: [
[
points[tris[i]],
points[tris[i + 1]],
points[tris[i + 2]],
points[tris[i]],
],
],
},
});
}
return polys;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment