mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-04 02:14:01 +00:00
Traffic roads come from OpenFreeMap vector tiles, or TomTom flow tiles with a key (Hybrid by default). Military areas use OpenFreeMap polygons with a bundled worldwide name index; wide views show named points. ALPR reads an hourly OpenStreetMap extract. Cockpit place context and country, state and US county outlines use bundled Natural Earth and US Census data. Overpass is used only when an operator configures their own endpoint. Also: camera arrivals no longer land below ground, keyless terrain retries on 429, bounded relation geometry for configured Overpass, one shared OpenStreetMap credit, and separate tile and API transports for embedders. Co-authored-by: theGaryLarson <[email protected]> Co-authored-by: Milan Khanal <[email protected]>
85 lines
3.9 KiB
Python
85 lines
3.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Maintainer-only Overture military-name pack build (DuckDB 1.5.5)."""
|
|
import argparse
|
|
import gzip
|
|
import hashlib
|
|
import json
|
|
import math
|
|
from pathlib import Path
|
|
import re
|
|
|
|
ROOT = Path(__file__).resolve().parent.parent
|
|
RELEASE = '2026-09-23.1'
|
|
CLASSES = ['military_land', 'airfield', 'naval_base', 'range', 'barracks', 'base', 'training_area']
|
|
PACK = ROOT / 'src/data/local_data/osm_military_names'
|
|
|
|
|
|
def assemble(rows):
|
|
records = []
|
|
snapshots = set()
|
|
for row in rows:
|
|
source = next(s for s in row['sources'] if s['dataset'] == 'OpenStreetMap')
|
|
match = re.fullmatch(r'([nwr])(\d+)@\d+', source['record_id'])
|
|
if not match:
|
|
raise ValueError('Invalid typed OSM identity')
|
|
ident = match[1] + match[2]
|
|
bbox = [row['bbox'][k] for k in ['xmin', 'ymin', 'xmax', 'ymax']]
|
|
lon, lat, area = row['lon'], row['lat'], row['area']
|
|
if not all(math.isfinite(v) for v in [lon, lat, area, *bbox]):
|
|
raise ValueError('Invalid geometry measurement')
|
|
if not bbox[0] - 1e-7 <= lon <= bbox[2] + 1e-7 or not bbox[1] - 1e-7 <= lat <= bbox[3] + 1e-7:
|
|
raise ValueError('Label outside source bounds')
|
|
kind = row['military'] if row['military'] in CLASSES else 'military_land'
|
|
records.append([ident, row['name'], round(lon, 5), round(lat, 5),
|
|
*[round(v, 4) for v in bbox], CLASSES.index(kind), max(1, round(abs(area)))])
|
|
snapshots.add(source['version'][:10])
|
|
records.sort(key=lambda r: (r[0][0], int(r[0][1:])))
|
|
if len(set(r[0] for r in records)) != len(records):
|
|
raise ValueError('Duplicate OSM identity')
|
|
return {'release': RELEASE, 'snapshots': sorted(snapshots), 'classes': CLASSES, 'records': records}
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('--cache', type=Path, required=True)
|
|
parser.add_argument('--extensions', type=Path)
|
|
parser.add_argument('--verify', action='store_true')
|
|
args = parser.parse_args()
|
|
args.cache.mkdir(parents=True, exist_ok=True)
|
|
cached = args.cache / ('military-names-' + RELEASE + '.json')
|
|
if not cached.exists():
|
|
import duckdb
|
|
if duckdb.__version__ != '1.5.5':
|
|
raise RuntimeError('Use duckdb==1.5.5')
|
|
config = {'threads': '8', 'memory_limit': '3GB', 'temp_directory': str(args.cache / 'temp')}
|
|
if args.extensions:
|
|
config['extension_directory'] = str(args.extensions)
|
|
connection = duckdb.connect(config=config)
|
|
if not args.extensions:
|
|
connection.execute('INSTALL httpfs; INSTALL spatial;')
|
|
connection.execute('LOAD httpfs; LOAD spatial; SET geometry_always_xy=true; SET enable_progress_bar=false;')
|
|
files = json.loads((ROOT / 'scripts/military-names-files.json').read_text())
|
|
if not all('/' + RELEASE + '/' in url for url in files):
|
|
raise ValueError('Unexpected source release')
|
|
cursor = connection.execute((ROOT / 'scripts/military-names.sql').read_text(), [files])
|
|
fields = [d[0] for d in cursor.description]
|
|
rows = [dict(zip(fields, row)) for row in cursor.fetchall()]
|
|
cached.write_text(json.dumps(rows, ensure_ascii=False, separators=(',', ':')))
|
|
pack = assemble(json.loads(cached.read_text()))
|
|
raw = json.dumps(pack, ensure_ascii=False, separators=(',', ':')).encode()
|
|
digest = hashlib.sha256(raw).hexdigest()
|
|
if args.verify:
|
|
expected = (PACK / 'names.sha256').read_text().split()[0]
|
|
if digest != expected:
|
|
raise ValueError(f'Output SHA-256 mismatch: {digest}')
|
|
else:
|
|
PACK.mkdir(parents=True, exist_ok=True)
|
|
(PACK / 'names.json').write_bytes(raw)
|
|
(PACK / 'names.sha256').write_text(digest + ' names.json\n')
|
|
print(json.dumps({'records': len(pack['records']), 'rawBytes': len(raw),
|
|
'gzipBytes': len(gzip.compress(raw, compresslevel=9, mtime=0)), 'sha256': digest}))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|