scripts/clean-geom.js (82 lines of code) (raw):

/* * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ import fs from "node:fs"; import GeoJSONReader from "jsts/org/locationtech/jts/io/GeoJSONReader.js"; import GeoJSONWriter from "jsts/org/locationtech/jts/io/GeoJSONWriter.js"; import IsSimpleOp from "jsts/org/locationtech/jts/operation/IsSimpleOp.js"; import IsValidOp from "jsts/org/locationtech/jts/operation/valid/IsValidOp.js"; import { BufferOp } from "jsts/org/locationtech/jts/operation/buffer.js"; import rewind from "geojson-rewind"; import yargs from "yargs/yargs"; const argv = yargs(process.argv.slice(2)) .version() .option('verbose', { alias: 'v', default: false, type: 'boolean', describe: 'Log about the process', }) .option('output', { alias: 'o', type: 'string', describe: 'Write the output GeoJSON in a different path', }) .demandCommand(1) .epilog('Elastic, 2019') .example('$0 in.geojson', 'Overwrites your file') .example('$0 -o fix.geojson in.geojson', 'Leaves your input file as is') .argv; const filePath = argv._[0]; function log(message) { if (argv.verbose) { console.log(message); } } if (!filePath) { throw new Error(`Add the GeoJSON file path to clean. e.g. node scripts/clean-geom.js data/usa_states_v1.geo.json`); } else { log('Processing ' + filePath); } function makeValid(feature) { const writer = new GeoJSONWriter(); const newFeature = { type: 'Feature', geometry: null, properties: feature.properties, }; if (feature.id) newFeature.id = feature.id; const isSimple = new IsSimpleOp(feature.geometry); const isValid = new IsValidOp(feature.geometry); if (!isSimple.isSimple() || !isValid.isValid()) { if (!isValid.isValid()) { log(`Feature [${feature.id}] is invalid`); } const geom = BufferOp.bufferOp(feature.geometry,0); const geomArea = geom.getArea(); if (geomArea === 0) { log(`New geometry is empty!!`); } else { log(`New geometry area: ${geomArea}`) } newFeature.geometry = writer.write(geom); } else { newFeature.geometry = writer.write(feature.geometry); } return newFeature; } const reader = new GeoJSONReader(); try { const fc = fs.readFileSync(filePath, 'utf8'); const gj = reader.read(fc); log(`Checking ${gj.features.length} features`); const features = gj.features.map(makeValid); // JSTS does not enforce winding order, so we pass the features through `geojson-rewind` // to wind them in clockwise order. const fixed = rewind({ type: 'FeatureCollection', features: features, }, true); const outputPath = argv.output || filePath; log(`Writing results to: ${outputPath}`); fs.writeFileSync(outputPath, JSON.stringify(fixed)); } catch (error) { log('Error processing your file'); log(error); process.exit(1); }