diff options
| author | Andrew Harvey <andrew@alantgeo.com.au> | 2021-06-18 21:02:41 +1000 | 
|---|---|---|
| committer | Andrew Harvey <andrew@alantgeo.com.au> | 2021-06-18 21:02:41 +1000 | 
| commit | c9a9be779a56bbb4af2375b9702d762ebf43fefe (patch) | |
| tree | ed743b424d358df7df439541268eb98318e492c5 /bin | |
| parent | 2db70ba5db5eebd292ac08a0d10228b118bdbf27 (diff) | |
report overlaps
Diffstat (limited to 'bin')
| -rwxr-xr-x | bin/reportOverlap.js | 127 | 
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) +          } +        } +      ) +    } +  } +)  | 
