summaryrefslogtreecommitdiffhomepage
path: root/gui/scripts/prepare-rtree.ts
blob: 7ec108cc7520bb1119044cd06bfbd9c4b341378c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
//
// Script that generates r-trees for geo data.
// run with `npx ts-node geo-data/prepare-rtree.ts`
//

import * as fs from 'fs';
import * as path from 'path';
import { Topology, GeometryCollection } from 'topojson-specification';
import { GeoJSON } from 'geojson';
import rbush from 'rbush';

interface GeometryTopologyObjects {
  [key: string]: any;
  geometry: GeometryCollection;
}

function main() {
  const GEOMETRY_DATA_FILES = ['geometry', 'states-provinces-lines'];
  const POINT_DATA_FILES = ['countries', 'cities'];
  const OUTPUT_DIR = path.join(__dirname, 'out');

  for (const name of GEOMETRY_DATA_FILES) {
    const source = path.join(OUTPUT_DIR, `${name}.json`);
    const destination = path.join(OUTPUT_DIR, `${name}.rbush.json`);

    try {
      processGeometry(source, destination);
    } catch (error) {
      console.error(`Failed to process ${name}: ${error.message}`);
    }
  }

  for (const name of POINT_DATA_FILES) {
    const source = path.join(OUTPUT_DIR, `${name}.json`);
    const destination = path.join(OUTPUT_DIR, `${name}.rbush.json`);

    try {
      processPoints(source, destination);
    } catch (error) {
      console.error(`Failed to process ${name}: ${error.message}`);
    }
  }
}

function processGeometry(source: string, destination: string) {
  const collection = JSON.parse(fs.readFileSync(source, { encoding: 'utf8' })) as Topology<
    GeometryTopologyObjects
  >;

  const { geometry } = collection.objects;
  const treeData = geometry.geometries.map((object, i) => {
    if (!object.bbox) {
      throw new Error(`Expected a geometry at index ${i} to have a bbox property.`);
    }

    const [minX, minY, maxX, maxY] = object.bbox;
    return {
      ...object,
      minX,
      minY,
      maxX,
      maxY,
    };
  });

  const tree = rbush();
  tree.load(treeData);
  fs.writeFileSync(destination, JSON.stringify(tree.toJSON()));

  console.log(`Saved a rbush to ${destination}`);
}

function processPoints(source: string, destination: string) {
  const collection = JSON.parse(fs.readFileSync(source, { encoding: 'utf8' })) as GeoJSON;

  if (collection.type !== 'FeatureCollection') {
    throw new Error(
      `Invalid collection type ${collection.type} in ${source}. Expected FeatureCollection`,
    );
  }

  const treeData = collection.features.map((feat) => {
    if (feat.geometry.type !== 'Point') {
      throw new Error(`Invalid geometry in ${source}. Expected "Point", got ${feat.geometry.type}`);
    }

    const { coordinates } = feat.geometry;
    return {
      ...feat,
      minX: coordinates[0],
      minY: coordinates[1],
      maxX: coordinates[0],
      maxY: coordinates[1],
    };
  });

  const tree = rbush();
  tree.load(treeData);
  fs.writeFileSync(destination, JSON.stringify(tree.toJSON()));

  console.log(`Saved a rbush to ${destination}`);
}

main();