Skip to content

geojson: holes, features with no place, both types of id, and bbox - #172

Merged
donislawdev merged 2 commits into
mainfrom
format/geojson-holes
Oct 7, 2026
Merged

donislawdev merged 2 commits into
mainfrom
format/geojson-holes

Conversation

@donislawdev

@donislawdev donislawdev commented Oct 7, 2026 •

Copy link
Copy Markdown
Owner

Four new settings for the geojson format, all off by default - files made with the earlier settings keep their bytes (the six existing golden cases are unchanged).

What a user gets

  • holes (0 to 100 000): every polygon gets that many holes of four points, each running the other way round to its outline as RFC 7946 asks. Needs 6 or more vertices. Fewer fit at precision 3 or less, and asking for more is refused with the number that fits.
  • unlocated (none, some, all): every fifth feature, or every one, has a geometry of null.
  • ids (number, string, mixed, none): the id as a number (as before), as a string such as f2, both in turn, or left out. Measured: GDAL keeps only the numbers of a mixed file and warns that several features share an id.
  • bbox: the collection and every feature with a place carry the box their coordinates lie in. The collection's box comes after its features (its extent is known only after the last one). A box across the antimeridian has its west edge greater than its east edge (RFC 7946 §5.2), and one whose shapes reach all the way round is -180 to 180 (§5.3).

How it is checked

  • GDAL ignores bbox entirely (a wrong box and one of three numbers both opened without a word), and neither GDAL nor shapely looks at which way a hole runs. The structural checker (check_geojson) is the witness for both: it rebuilds every box from the positions and checks every hole's winding. It also checks that no edge spans half the globe, and it reports the shape of the collection's box so a guard can assert that a crossing box and one round the globe were reached.
  • The shapely check skips null geometries and unwraps a whole shape from one anchor, so a hole past 180 stays inside its outline.
  • 15 more cases in the reference-tool guard (GDAL + shapely + checker), a refusal guard for holes, a guard for the two box states, and the carried-forward minimum varied over the new settings. Four new golden cases.
  • A probe over 107 combinations at two sizes found two construction defects before the guards existed: an outline widened for holes had edges longer than half the globe, and a one-step margin round the holes is below double precision at 15 decimal places. Both are fixed by construction.

Not done here

  • Looking at the files in QGIS by eye (step 7) - the owner's.
  • Mutation proofs of the new entries (run on request only).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • GeoJSON generation now supports polygon holes, unlocated features, configurable numeric or string IDs (including mixed or omitted IDs), and feature and collection bounding boxes.
    • New settings are available in the app and documented with their limits.
  • Bug Fixes
    • Improved handling of bounding boxes that cross the antimeridian or span the globe.
    • Corrected validation of polygon holes and features without geometry.

Four settings, all off by default, so files made with the earlier
settings keep their bytes (the six golden cases are unchanged).

- holes: up to 100 000 holes of four points in every polygon, each
  running the other way round to its outline. An outline with holes
  keeps a band round its middle free by construction, and every hole
  keeps a share of its cell clear - a one-step margin is below what a
  double tells apart at fifteen places. An outline widened for holes
  spans at most half the globe, so no edge reads as the way round the
  other side. Needs 6 or more vertices.
- unlocated: every fifth feature (some) or every one (all) has a
  geometry of null.
- ids: number, string (f2), mixed or none. GDAL keeps only the numbers
  of a mixed file and warns about shared ids - the guard allows that
  warning and nothing else.
- bbox: on the collection and every feature with a place, the
  collection's after its features because the generator streams. A box
  across the antimeridian has its west edge greater than its east edge,
  and one round the globe is -180 to 180. GDAL ignores bbox, so the
  structural checker rebuilds every box from the positions.

The shapely check skips null geometries and unwraps a whole shape from
one anchor, so a hole past 180 stays inside its outline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Repository UI (base), Organization UI (inherited)
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 4318b0f4-053a-459e-9bff-8a781a8b0406

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI (base), Organization UI (inherited)
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: af52de24-d600-4140-8c76-7a8367c41b9b
📥 Commits

Reviewing files that changed from the base of the PR and between a54cbcf and 26736a7.

⛔ Files ignored due to path filters (22)
  • web/public/ar/formats/index.html is excluded by !**/web/public/**
  • web/public/cs/formaty/index.html is excluded by !**/web/public/**
  • web/public/de/formate/index.html is excluded by !**/web/public/**
  • web/public/es/formatos/index.html is excluded by !**/web/public/**
  • web/public/formats/index.html is excluded by !**/web/public/**
  • web/public/fr/formats/index.html is excluded by !**/web/public/**
  • web/public/hi/formats/index.html is excluded by !**/web/public/**
  • web/public/id/format/index.html is excluded by !**/web/public/**
  • web/public/it/formati/index.html is excluded by !**/web/public/**
  • web/public/ja/formats/index.html is excluded by !**/web/public/**
  • web/public/ko/formats/index.html is excluded by !**/web/public/**
  • web/public/nl/formaten/index.html is excluded by !**/web/public/**
  • web/public/pl/formaty/index.html is excluded by !**/web/public/**
  • web/public/pt-br/formatos/index.html is excluded by !**/web/public/**
  • web/public/ro/formate/index.html is excluded by !**/web/public/**
  • web/public/ru/formats/index.html is excluded by !**/web/public/**
  • web/public/th/formats/index.html is excluded by !**/web/public/**
  • web/public/tr/bicimler/index.html is excluded by !**/web/public/**
  • web/public/uk/formats/index.html is excluded by !**/web/public/**
  • web/public/vi/dinh-dang/index.html is excluded by !**/web/public/**
  • web/public/zh-hans/formats/index.html is excluded by !**/web/public/**
  • web/public/zh-hant/formats/index.html is excluded by !**/web/public/**
📒 Files selected for processing (40)
  • CHANGELOG.md
  • README.md
  • internal/format/geojson/feature.go
  • internal/format/geojson/geojson.go
  • internal/format/geojson/geometry.go
  • internal/format/geojson/layout.go
  • internal/format/geojson/settings.go
  • internal/format/geojson/shape.go
  • internal/guard/generatorbytes_test.go
  • internal/guard/geojson_test.go
  • internal/guard/parity_test.go
  • internal/guard/testdata/generator-golden.json
  • internal/gui/text/locale/registry/en.json
  • internal/gui/text/locale/registry/pl.json
  • internal/gui/text/locale/said/en.json
  • internal/gui/text/locale/said/pl.json
  • internal/oracle/geoscripts.go
  • internal/oracle/strict.py
  • web/content/ar/site.json
  • web/content/cs/site.json
  • web/content/de/site.json
  • web/content/en/site.json
  • web/content/es/site.json
  • web/content/fr/site.json
  • web/content/hi/site.json
  • web/content/id/site.json
  • web/content/it/site.json
  • web/content/ja/site.json
  • web/content/ko/site.json
  • web/content/nl/site.json
  • web/content/pl/site.json
  • web/content/pt-BR/site.json
  • web/content/ro/site.json
  • web/content/ru/site.json
  • web/content/th/site.json
  • web/content/tr/site.json
  • web/content/uk/site.json
  • web/content/vi/site.json
  • web/content/zh-Hans/site.json
  • web/content/zh-Hant/site.json

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

📜 Recent review details
⏰ Context from checks skipped due to timeout. (16)
  • GitHub Check: Analyze (python)
  • GitHub Check: Analyze (go)
  • GitHub Check: Analyze (actions)
  • GitHub Check: bill of materials
  • GitHub Check: reference tools actually installed
  • GitHub Check: test on macos-latest
  • GitHub Check: coverage gate
  • GitHub Check: the Chocolatey packages install and leave
  • GitHub Check: test on windows-latest
  • GitHub Check: the installer installs and leaves
  • GitHub Check: semgrep
  • GitHub Check: linters
  • GitHub Check: test on ubuntu-latest
  • GitHub Check: import table of the window binary
  • GitHub Check: known vulnerabilities
  • GitHub Check: staticcheck
🧰 Additional context used
📚 Code guidelines (1)
CONTRIBUTING.md — configured
📓 Path-based instructions (15)
Applies to text shown to the user (labels, buttons, tooltips, placeholders, dialogs, errors, status messages, empty states, translations).

⚙️ CodeRabbit configuration file

Files:

  • internal/guard/parity_test.go
  • internal/gui/text/locale/said/pl.json
  • internal/gui/text/locale/registry/pl.json
  • internal/gui/text/locale/said/en.json
  • internal/guard/generatorbytes_test.go
  • internal/oracle/geoscripts.go
  • internal/gui/text/locale/registry/en.json
  • internal/format/geojson/geojson.go
  • internal/format/geojson/layout.go
  • internal/format/geojson/feature.go
  • internal/format/geojson/geometry.go
  • internal/format/geojson/settings.go
  • internal/format/geojson/shape.go
  • internal/guard/geojson_test.go
  • internal/oracle/strict.py
Verify tests check real behavior and would fail if the implementation were broken.

⚙️ CodeRabbit configuration file

Files:

  • internal/guard/parity_test.go
  • internal/guard/generatorbytes_test.go
  • internal/guard/geojson_test.go
These are end-user desktop applications.

⚙️ CodeRabbit configuration file

Files:

  • internal/guard/parity_test.go
  • internal/guard/generatorbytes_test.go
  • internal/oracle/geoscripts.go
  • internal/format/geojson/geojson.go
  • internal/format/geojson/layout.go
  • internal/format/geojson/feature.go
  • internal/format/geojson/geometry.go
  • internal/format/geojson/settings.go
  • internal/format/geojson/shape.go
  • internal/guard/geojson_test.go
  • internal/oracle/strict.py
Performance is a known weak spot of these projects.

⚙️ CodeRabbit configuration file

Files:

  • internal/guard/parity_test.go
  • internal/guard/generatorbytes_test.go
  • internal/oracle/geoscripts.go
  • internal/format/geojson/geojson.go
  • internal/format/geojson/layout.go
  • internal/format/geojson/feature.go
  • internal/format/geojson/geometry.go
  • internal/format/geojson/settings.go
  • internal/format/geojson/shape.go
  • internal/guard/geojson_test.go
  • internal/oracle/strict.py
Applies only to code that builds or styles a GUI.

⚙️ CodeRabbit configuration file

Files:

  • internal/guard/parity_test.go
  • internal/guard/generatorbytes_test.go
  • internal/oracle/geoscripts.go
  • internal/format/geojson/geojson.go
  • internal/format/geojson/layout.go
  • internal/format/geojson/feature.go
  • internal/format/geojson/geometry.go
  • internal/format/geojson/settings.go
  • internal/format/geojson/shape.go
  • internal/guard/geojson_test.go
  • internal/oracle/strict.py
User-facing changelog.

⚙️ CodeRabbit configuration file

Files:

  • CHANGELOG.md
Domain: test file generator (Go; `tfg` CLI and `tfg-gui` Fyne window over one engine).

⚙️ CodeRabbit configuration file

Files:

  • internal/guard/testdata/generator-golden.json
  • internal/guard/parity_test.go
  • internal/gui/text/locale/said/pl.json
  • internal/gui/text/locale/registry/pl.json
  • internal/gui/text/locale/said/en.json
  • internal/guard/generatorbytes_test.go
  • internal/oracle/geoscripts.go
  • internal/gui/text/locale/registry/en.json
  • internal/format/geojson/geojson.go
  • internal/format/geojson/layout.go
  • internal/format/geojson/feature.go
  • internal/format/geojson/geometry.go
  • internal/format/geojson/settings.go
  • internal/format/geojson/shape.go
  • internal/guard/geojson_test.go
  • internal/oracle/strict.py
SECURITY, HIGH PRIORITY.

⚙️ CodeRabbit configuration file

Files:

  • internal/guard/parity_test.go
  • internal/guard/generatorbytes_test.go
  • internal/oracle/geoscripts.go
  • internal/format/geojson/geojson.go
  • internal/format/geojson/layout.go
  • internal/format/geojson/feature.go
  • internal/format/geojson/geometry.go
  • internal/format/geojson/settings.go
  • internal/format/geojson/shape.go
  • internal/guard/geojson_test.go
  • internal/oracle/strict.py
These apps are QA/developer tools.

⚙️ CodeRabbit configuration file

Files:

  • internal/guard/parity_test.go
  • internal/guard/generatorbytes_test.go
  • internal/oracle/geoscripts.go
  • internal/format/geojson/geojson.go
  • internal/format/geojson/layout.go
  • internal/format/geojson/feature.go
  • internal/format/geojson/geometry.go
  • internal/format/geojson/settings.go
  • internal/format/geojson/shape.go
  • internal/guard/geojson_test.go
  • internal/oracle/strict.py
Source of the public project website (generated output is excluded from review).

⚙️ CodeRabbit configuration file

Files:

  • web/content/hi/site.json
  • web/content/de/site.json
  • web/content/ro/site.json
  • web/content/it/site.json
  • web/content/ar/site.json
  • web/content/ja/site.json
  • web/content/en/site.json
  • web/content/zh-Hant/site.json
  • web/content/fr/site.json
  • web/content/id/site.json
  • web/content/tr/site.json
  • web/content/pt-BR/site.json
  • web/content/nl/site.json
  • web/content/zh-Hans/site.json
  • web/content/uk/site.json
  • web/content/th/site.json
  • web/content/es/site.json
  • web/content/cs/site.json
  • web/content/pl/site.json
  • web/content/ru/site.json
  • web/content/vi/site.json
  • web/content/ko/site.json
Go code.

⚙️ CodeRabbit configuration file

Files:

  • internal/guard/parity_test.go
  • internal/guard/generatorbytes_test.go
  • internal/oracle/geoscripts.go
  • internal/format/geojson/geojson.go
  • internal/format/geojson/layout.go
  • internal/format/geojson/feature.go
  • internal/format/geojson/geometry.go
  • internal/format/geojson/settings.go
  • internal/format/geojson/shape.go
  • internal/guard/geojson_test.go
Check that documentation matches the actual code in this PR: commands, flags, config keys, file paths, build steps and examples must exist.

⚙️ CodeRabbit configuration file

Files:

  • README.md
  • CHANGELOG.md
Python code.

⚙️ CodeRabbit configuration file

Files:

  • internal/oracle/strict.py
All code in this repository is written by an AI coding agent (Claude Code).

⚙️ CodeRabbit configuration file

Files:

  • web/content/hi/site.json
  • web/content/de/site.json
  • web/content/ro/site.json
  • web/content/it/site.json
  • web/content/ar/site.json
  • web/content/ja/site.json
  • web/content/en/site.json
  • web/content/zh-Hant/site.json
  • web/content/fr/site.json
  • README.md
  • web/content/id/site.json
  • web/content/tr/site.json
  • web/content/pt-BR/site.json
  • web/content/nl/site.json
  • web/content/zh-Hans/site.json
  • web/content/uk/site.json
  • web/content/th/site.json
  • web/content/es/site.json
  • internal/guard/testdata/generator-golden.json
  • web/content/cs/site.json
  • web/content/pl/site.json
  • internal/guard/parity_test.go
  • internal/gui/text/locale/said/pl.json
  • CHANGELOG.md
  • web/content/ru/site.json
  • internal/gui/text/locale/registry/pl.json
  • internal/gui/text/locale/said/en.json
  • web/content/vi/site.json
  • internal/guard/generatorbytes_test.go
  • internal/oracle/geoscripts.go
  • internal/gui/text/locale/registry/en.json
  • internal/format/geojson/geojson.go
  • internal/format/geojson/layout.go
  • internal/format/geojson/feature.go
  • internal/format/geojson/geometry.go
  • internal/format/geojson/settings.go
  • internal/format/geojson/shape.go
  • internal/guard/geojson_test.go
  • web/content/ko/site.json
  • internal/oracle/strict.py
Source excerpt: **Words a user reads are English, with a flat hyphen and no semicolons.**

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Files:

  • README.md
  • CHANGELOG.md
🪛 LanguageTool
CHANGELOG.md

[style] ~93-~93: In formal contexts, the form “around” is more common
Context: ...ur points, each running the other way round to its outline as RFC 7946 asks. Holes ...

(ROUND_AROUND)


📝 Walkthrough

Walkthrough

GeoJSON generation now supports polygon holes, unlocated features, configurable feature IDs, and feature and collection bounding boxes. Settings, geometry serialization, validation, reference-tool checks, interface text, and documentation cover these options.

Changes

GeoJSON output

Layer / File(s) Summary
Settings and exposed options
internal/format/geojson/settings.go, internal/format/geojson/geojson.go, internal/gui/text/locale/registry/*, internal/gui/text/locale/said/*, web/content/*/site.json, README.md, CHANGELOG.md
Settings add holes, unlocated, ids, and bbox, with defaults, limits, and refusal messages. The plan properties, interface text, translations, and documentation describe these options.
Geometry and extent construction
internal/format/geojson/shape.go
Geometry kinds include unlocated features. Polygon drawing supports holes, and extent tracking records coordinate bounds.
Feature and collection serialization
internal/format/geojson/feature.go, internal/format/geojson/geometry.go, internal/format/geojson/layout.go
Features can emit numeric, string, or omitted IDs and optional geometry bounding boxes. Collection output can include an accumulated bbox. Longitude coordinates are wrapped during serialization.
Validation and reference-tool checks
internal/oracle/strict.py, internal/oracle/geoscripts.go, internal/guard/geojson_test.go, internal/guard/generatorbytes_test.go, internal/guard/parity_test.go, internal/guard/testdata/generator-golden.json
The strict checker and Shapely script handle the added GeoJSON forms. Tests cover hole limits, bbox cases, feature reads, reachable settings, and byte-size golden cases.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~60 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant GeoJSONSettings
  participant records
  participant drawing
  participant emitter
  GeoJSONSettings->>records: provide geometry, ID, and bbox settings
  records->>drawing: draw feature and accumulate extent
  records->>emitter: write feature ID and optional feature bbox
  records->>emitter: write collection bbox when an extent is set
Loading

Suggested labels: enhancement, ui

Merge Risk: ⚪ Minimal · up to 26736

No actionable issue identified in the new GeoJSON options; mergeable after normal checks.

🚥 Pre-merge checks | ✅ 13 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Safe File Parsing ⚠️ Warning internal/oracle/strict.py:geo_drawn materializes every position of each feature into drawn. geo_feature calls it for every located feature, even when bbox is false. The new settings allow up t… Replace geo_drawn with a streaming extent calculation. Update longitude and latitude minima and maxima while iterating through positions, and do not retain a list for the feature. Stream edge checks as adjacent positions instead of buildi…
✅ Passed checks (13 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the user-facing GeoJSON additions: polygon holes, unlocated features, identifier variants, and bounding boxes. It is specific, concise, and within the length limit.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Tests For Changed Behavior ✅ Passed PASS: The PR changes GeoJSON runtime behavior and adds direct test coverage. internal/guard/geojson_test.go adds cases for holes, unlocated features, all ID modes, bboxes, antimeridian handling, mix…
No Secrets Or Debug Leftovers ✅ Passed No prohibited agent files or .env files were added. Added-line scans found no credentials, private URLs, absolute local paths, internal hostnames/IPs, or personal email addresses. The only added `pr…
No Hardcoded Ui Styling ✅ Passed The PR does not add or change GUI implementation code. Its internal/gui changes are locale JSON text only, and its web/public changes add generated HTML table rows without styling declarations. Th…
No Obvious Performance Problems ✅ Passed No clear performance problem matches the check. GeoJSON hole generation and bbox calculation are linear in the coordinates requested and in the bytes emitted; no O(n²) collection processing was introd…
Desktop Robustness ✅ Passed No explicit Desktop robustness failure is introduced. The diff adds GeoJSON generation, validation, tests, documentation, and locale text; it adds no working-directory asset loads, settings-file persi…
System Changes Are Reversible ✅ Passed The pull request changes GeoJSON generation, validation, tests, localization, and documentation. The changed implementation only builds in-memory GeoJSON bytes and settings; the added-code search foun…
Clear User-Facing Text ✅ Passed PASS — The PR adds user-facing GeoJSON labels, choices, setting explanations, and refusal messages. The new controls have descriptive detail text, and numeric holes includes the 0–100000 holes ran…
No Resource Leaks ✅ Passed No resource leak is introduced. The changed GeoJSON generator uses bounded per-write buffers and reuses them: drawing.reset truncates pos and ends, while records reuses tail and closing. T…
Scope, Duplication And Docs ✅ Passed The PR scope matches the title and description: it adds the four GeoJSON settings, related generation logic, validators, guards, translations, and documentation. The diff contains no unrelated refacto…
Full details: Safe File Parsing

Explanation

internal/oracle/strict.py:geo_drawn materializes every position of each feature into drawn. geo_feature calls it for every located feature, even when bbox is false. The new settings allow up to 1,000,000 vertices and 100,000 holes, so one supported polygon can create about 1.5 million position lists and exhaust the checker process memory. geo_lon, geo_edges, and geo_bbox also compute 10 ** settings["precision"] without validating the precision in check_geojson; a direct caller can cause excessive integer allocation. The JSON APIs do not execute code or resolve entities, but the new validation path is not safe for huge input or untrusted settings.

Resolution

Replace geo_drawn with a streaming extent calculation. Update longitude and latitude minima and maxima while iterating through positions, and do not retain a list for the feature. Stream edge checks as adjacent positions instead of building xs where possible. In check_geojson, validate precision against the format limit (0 through 15) before any 10 ** precision operation. Add an input-size and nesting/feature-position limit before json.loads, or reject inputs above the supported GeoJSON limits, so malformed or oversized files fail with a controlled validation error rather than exhausting memory.

  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot added enhancement New feature or request ui labels Oct 7, 2026
…ecker's box only when asked

The smallest closing feature built the end of the collection into a new
buffer for every kind and count it measured - fourteen allocations a file
with mixed, past the ceiling of the guard that keeps a file out of memory.
It now reuses the buffer the closing feature already has.

The structural checker worked out every feature's box even with bbox off
and kept a copy of every position to do it. It now keeps a running minimum
and maximum, only when a bbox was ordered, and walks edges a pair at a
time. Measured, the copy did not set the checker's peak - the parsed
document and the rebuilt layout do - so the comment says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@donislawdev
donislawdev merged commit aef7557 into main Oct 7, 2026
22 checks passed
@donislawdev
donislawdev deleted the format/geojson-holes branch October 7, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request ui

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant