Skip to content

feat(server): named areas from a GeoJSON file (Data tab 1/6) - #14

Closed
A13xB0 wants to merge 4 commits into
masterfrom
feat/data-areas-geojson
Closed

A13xB0 wants to merge 4 commits into
masterfrom
feat/data-areas-geojson

Conversation

@A13xB0

@A13xB0 A13xB0 commented Oct 10, 2026

Copy link
Copy Markdown
Owner

First of six PRs for the Data tab. The plan is at https://claude.ai/artifact/PAJwg4KmQQ7Cp7nSDvyBo9.

This PR lets CoreScope load named areas from a GeoJSON file. They work wherever areas already works today, and later PRs build the Data tab's "origin area" on top of them. No UI changes are in this PR.

What changes

  • New package internal/areamap (its own module, with the Dockerfile COPY line added):
    • Parses a GeoJSON FeatureCollection of Polygon and MultiPolygon features.
    • Holes and islands are respected (even-odd rule).
    • Each feature has code (required), label, layer (default region) and an optional iata list.
    • Locate(layer, lat, lon) returns the smallest area that contains the point, so a city inside a country resolves to the city.
    • (0,0) is never treated as a place.
    • Duplicate codes are refused, across layers too.
  • New config key areasFile: a path to the GeoJSON file, relative to the config dir unless absolute. It is optional and empty by default.
    • Existing areas entries (bbox or polygon) are merged into the same map.
    • A code that appears in both places is refused at startup with a clear error.
  • /api/config/areas lists file areas next to config areas, and each entry gains a layer field.
  • The area= filter (nodes, packets, and so on) accepts file area codes through the same cached resolver.
  • Docs: docs/examples/areas.example.geojson, a config.example.json entry, and the api-spec.md update.

Tests

  • Red before green, two pairs of commits:
  • internal/areamap tests cover:
    • parsing, and lon/lat order;
    • a MultiPolygon with a hole and an island;
    • layers, and smallest-wins;
    • IATA lookup, Has and Contains;
    • bad input and duplicate codes.
  • cmd/server tests cover:
    • the config and the file merging;
    • bad files refused (duplicate code, missing file, invalid JSON);
    • /api/config/areas showing layers;
    • area= filtering on a file area;
    • the documented example file loading.
  • Repo checks:
    • full server suite passes;
    • scripts/allmod.sh passes 17 of 17;
    • gofmt is clean;
    • check-dockerfile-internal-pkgs.sh passes.
  • Manual check: I ran the server against a copy of a real DB with the example file.
    • It logged [areas] 2 areas in layers [region].
    • /api/config/areas returned BAY and MRY.
    • /api/nodes?area=BAY returned only Bay Area nodes.

Benchmark

BenchmarkLocate   2.1 µs/op   0 B/op   0 allocs/op

The benchmark uses a three-area fixture, including a MultiPolygon with a hole. A bounding-box check runs before the ring test.

Gate

  • With areasFile unset, the code paths behave as before.
  • Config-only areas take the existing PassesFilter route.
  • Pre-prod has no areas configured today, so this change is inert there until PR 6 adds ScotMesh's areas.geojson.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KaR4xu8gVykQBkH8yG8m6H

A13xB0 and others added 4 commits October 10, 2026 13:49
A new internal package that places a position in a named area drawn in a
GeoJSON file: Polygon and MultiPolygon with holes, [lon, lat] order, a
layer per feature, the smallest area of a layer winning where areas
overlap, airport codes for observers without a position, and unique codes
across layers. Stubs only; every test fails on its assertions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KaR4xu8gVykQBkH8yG8m6H
ParseGeoJSON reads a FeatureCollection (Polygon and MultiPolygon, holes
respected, [lon, lat] order); each feature's properties give its code,
label, layer (default "region") and the airport codes of observers that
report no position. New refuses a code used twice, in any layer.

Locate checks each polygon's bounding box first, then the even-odd test on
its outer ring and holes, and returns the smallest containing area of the
layer, so nested areas resolve to the most specific. Size is the shoelace
area with longitude scaled by latitude: enough to order overlaps.

BenchmarkLocate (six areas of 2,000 vertices): 2.1 us/op, 0 allocs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KaR4xu8gVykQBkH8yG8m6H
areasFile names a GeoJSON FeatureCollection next to config.json. Its areas
join config.json's (converted from their [lat, lon] order) in one map kept
on the config; a code in both, a missing file or bad JSON are refused.
/api/config/areas lists file areas with their layer, and area= accepts a
file area's code. Stubs only: the tests fail on their assertions.

Also wires internal/areamap into the server module and the Dockerfile, and
adds Map.Has to the package (red there too).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KaR4xu8gVykQBkH8yG8m6H
config.json's areas are one polygon or box each, which cannot hold a
coastline, islands or a hole. areasFile names a GeoJSON FeatureCollection
beside config.json whose features become areas too: Polygon and
MultiPolygon with holes, a layer per feature, airport codes for observers
that report no position.

- LoadAreaMap builds one map from config.json's areas (layer "region",
  converted from their [lat, lon] order) and the file, and keeps it on the
  config. A code in both, a missing file or bad GeoJSON stops startup with
  the reason, rather than running with areas silently missing.
- /api/config/areas lists file areas after config.json's, each entry now
  carrying its layer ("region" for config.json areas). Additive: existing
  fields unchanged.
- area= accepts a file area's code, matched by node position against its
  geometry through the same cache. config.json areas keep their existing
  polygon and box path unchanged.

Documented in config.example.json (areasFile + _comment_areasFile),
docs/api-spec.md, and an example file, docs/examples/areas.example.geojson,
which a test loads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KaR4xu8gVykQBkH8yG8m6H
@A13xB0

A13xB0 commented Oct 10, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #16, which carries this as its own commits alongside the rest of the Data tab (one PR, as asked).

@A13xB0 A13xB0 closed this Oct 10, 2026
@A13xB0
A13xB0 deleted the feat/data-areas-geojson branch October 10, 2026 14:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant