aboutsummaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorAndrew Harvey <andrew@alantgeo.com.au>2021-06-18 21:02:41 +1000
committerAndrew Harvey <andrew@alantgeo.com.au>2021-06-18 21:02:41 +1000
commitc9a9be779a56bbb4af2375b9702d762ebf43fefe (patch)
treeed743b424d358df7df439541268eb98318e492c5 /bin
parent2db70ba5db5eebd292ac08a0d10228b118bdbf27 (diff)
report overlaps
Diffstat (limited to 'bin')
-rwxr-xr-xbin/reportOverlap.js127
1 files changed, 127 insertions, 0 deletions
diff --git a/bin/reportOverlap.js b/bin/reportOverlap.js
new file mode 100755
index 0000000..902d036
--- /dev/null
+++ b/bin/reportOverlap.js
@@ -0,0 +1,127 @@
+#!/usr/bin/env node
+
+/**
+ * Report features which overlap
+ */
+
+const fs = require('fs')
+const { Readable, Transform, pipeline } = require('stream')
+const ndjson = require('ndjson')
+
+const argv = require('yargs/yargs')(process.argv.slice(2))
+ .argv
+
+if (argv._.length < 2) {
+ console.error("Usage: ./reportOverlap.js input.geojson output.geojson")
+ process.exit(1)
+}
+
+const inputFile = argv._[0]
+const outputFile = argv._[1]
+
+if (!fs.existsSync(inputFile)) {
+ console.error(`${inputFile} not found`)
+ process.exit(1)
+}
+
+let sourceCount = 0
+const features = {}
+
+/**
+ * Index features by geometry. Used as a first pass, so a second pass can easily compare
+ * features with the same geometry.
+ */
+const index = new Transform({
+ readableObjectMode: true,
+ writableObjectMode: true,
+ transform(feature, encoding, callback) {
+ sourceCount++
+
+ if (!argv.quiet) {
+ if (process.stdout.isTTY && sourceCount % 10000 === 0) {
+ process.stdout.write(` ${sourceCount.toLocaleString()}\r`)
+ }
+ }
+
+ const geometryKey = feature.geometry.coordinates.join(',')
+
+ if (!(geometryKey in features)) {
+ features[geometryKey] = []
+ }
+ features[geometryKey].push(feature)
+
+ callback()
+ }
+})
+
+let totalFeaturesWhichOverlap = 0
+let countGroupsOfOverlaps = 0
+
+/**
+ * Report features with the same geometry.
+ */
+let featureIndex = 0
+const reportOverlap = new Transform({
+ readableObjectMode: true,
+ writableObjectMode: true,
+ transform(key, encoding, callback) {
+ featureIndex++
+ if (!argv.quiet) {
+ if (process.stdout.isTTY && featureIndex % 10000 === 0) {
+ process.stdout.write(` ${featureIndex.toLocaleString()} / ${sourceCount.toLocaleString()} (${Math.round(featureIndex / sourceCount * 100)}%)\r`)
+ }
+ }
+
+ const sharedGeometry = features[key]
+
+ if (sharedGeometry.length === 1) {
+ // only one feature with this geometry
+ } else {
+ totalFeaturesWhichOverlap += sharedGeometry.length
+ countGroupsOfOverlaps++
+ this.push({
+ type: 'Feature',
+ properties: {
+ count: sharedGeometry.length
+ },
+ geometry: sharedGeometry[0].geometry
+ })
+ }
+
+ callback()
+ }
+})
+
+// first pass to index by geometry
+console.log('Pass 1/2: index by geometry')
+pipeline(
+ fs.createReadStream(inputFile),
+ ndjson.parse(),
+ index,
+ err => {
+ if (err) {
+ console.log(err)
+ process.exit(1)
+ } else {
+ console.log(` of ${sourceCount.toLocaleString()} features found ${Object.keys(features).length.toLocaleString()} unique geometries`)
+ // second pass to report overlapping features
+ console.log('Pass 2/2: report overlapping features')
+ pipeline(
+ Readable.from(Object.keys(features)),
+ reportOverlap,
+ ndjson.stringify(),
+ fs.createWriteStream(outputFile),
+ err => {
+ if (err) {
+ console.log(err)
+ process.exit(1)
+ } else {
+ console.log(`Total overlapping features: ${totalFeaturesWhichOverlap}`)
+ console.log(`Locations with overlapping features: ${countGroupsOfOverlaps}`)
+ process.exit(0)
+ }
+ }
+ )
+ }
+ }
+)