Invalid Geometry at Feature 9,999 (GeoPackage)¶
A GeoPackage of 10,000 polygons in which exactly one -- the last -- is a self-intersecting bowtie. Every feature before it is a valid triangle, so a validity sweep that reads only the first batch, samples a head, or stops early reports the layer clean. The defect is placed past every plausible batch boundary deliberately: it is what separates a consumer that validates the data from one that validates the first page of it.
| Property | Value |
|---|---|
| Case ID | invalid_geometry_at_scale_gpkg |
| Category | vector |
| Format | GPKG |
| Geometry type | Polygon |
| CRS | EPSG:4326 |
| Location | Central Europe (synthetic lattice) — 10.00°E, 50.00°N → 10.50°E, 50.50°N |
| Test tier | unit |
| Size class | small |
| Storage class | bundled |
| Redistributable | yes |
| Loader | geopandas |
| Status | validated |
Use this case¶
import pytest
@pytest.mark.geocase_case("invalid_geometry_at_scale_gpkg")
def test_invalid_geometry_at_scale_gpkg(geocase_case) -> None:
data = geocase_case.load()
assert data is not None
Use GeoCase in your tests¶
Install the complete set of vector, raster, and NetCDF dependencies:
What this case checks¶
Expose consumers that validate a prefix rather than a dataset: a first-batch read, a head sample, or a paged sweep that stops before the final page all report this layer as fully valid. A consumer that reads every feature finds the bowtie at index 9,999.
Risk types covered¶
Expected behavior¶
| Assertion | Expected |
|---|---|
expect_loadable |
yes |
expect_valid_geometry |
no |
expect_crs |
yes |
expected_epsg |
4326 |
expected_geometry_types |
Polygon |
Notes¶
10,000 polygons on a regular lattice. The first 9,999 are valid triangles; the last is a self-intersecting bowtie.
Why the position is the case¶
Every other invalid-geometry case in the catalog holds one or two features, so the invalid one is also the first one. That makes them useless for the question this case asks: did the consumer validate the dataset, or the first page of it? With one feature there is no difference between the two, and a probe that cannot fail is a probe that terminates a search with a false green.
Index 9,999 is past every batch boundary a reader is likely to have. Any read of a prefix — one batch, a head sample, a paged sweep that stops early — reports the layer fully valid, and only a full read finds the defect.
The geometry is deliberately boring¶
A lattice of identical triangles, not a realistic landscape. The bowtie is the only interesting thing in the file, so a finding here cannot be confused with a geometric quirk somewhere else in the data. Nothing overlaps: the triangles are 0.003° across on a 0.005° grid.
The bowtie itself is the classic form — the ring's second and fourth vertices
swapped, so its two edges cross at the centre. GEOS, GDAL and PostGIS agree it
is invalid, which matters: a case whose invalidity is engine-dependent tests
the engine rather than the consumer. That is what
ambiguous_engine_dependent_polygon is for.
Generated, not committed¶
Built by scripts/generate_vector_fixtures.py (_large_specs), so it is
covered by the --check regeneration gate. hole_center_nodata drifted into
the exact inverse of its own description precisely because it was hand-written
and sat outside that gate.
The feature count comes from params.expected_feature_count in case.yaml, so
the number the generator writes and the number the content gate checks cannot
disagree.
Size¶
1.4 MB, written with SPATIAL_INDEX=NO. The R-tree would add roughly 750 KB —
more than half the payload again — to accelerate a query no case here performs.
The R-tree is covered deliberately by the SpatiaLite cases, at one feature each.
Required capabilities¶
loadgeometry-validation
Files¶
- Primary:
invalid_at_scale.gpkg - Notes:
notes.md
Source and license¶
- Source: geocase-curated
- License: MIT
Tags¶
batch_boundary invalid large polygon procedural vector
Related cases¶
- UTC Offset Changes at Row 10,000 (GeoPackage) --
mixed_timezone_after_batch_gpkg - First NULL After 10,000 Non-NULL Values (GeoPackage) --
null_after_batch_boundary_gpkg - Self-Intersecting Polygon --
self_intersecting_polygon - Unclosed Ring Polygon --
unclosed_ring_polygon - Out-of-Bounds / Invalid Coordinates --
out_of_bounds_coordinates