Skip to content

Empty Geometry in GeoPackage

vectorGPKGPointEPSG:4326tinybundled

A GeoPackage containing rows with empty geometries (WKB EMPTY representation) to test how loaders handle the distinction between NULL geometry and EMPTY geometry. In GeoPackage/SQLite, NULL means "no geometry value" while EMPTY means "a geometry that contains no coordinates". This case exposes loaders that conflate NULL and EMPTY or fail to handle EMPTY geometries correctly.

Point geometry of empty_geometry_gpkg, rendered from the case's data
Point geometry, rendered from the case's actual geometry. Scale is normalized to the viewport and is not comparable between cases.
Property Value
Case ID empty_geometry_gpkg
Category vector
Format GPKG
Geometry type Point
CRS EPSG:4326
Location Central Europe (synthetic) — 10.00°E, 50.00°N → 11.00°E, 51.00°N
Test tier unit
Size class tiny
Storage class bundled
Redistributable yes
Loader geopandas
Status validated

Use this case

import pytest


@pytest.mark.geocase_case("empty_geometry_gpkg")
def test_empty_geometry_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:

pip install "geocase[all]"

View GeoCase on PyPI.

What this case checks

Expose loaders that incorrectly handle EMPTY geometries in GeoPackage format. Detect confusion between SQL NULL and WKB EMPTY geometry representations. Test filtering and query behavior with empty geometries.

Risk types covered

Expected behavior

Assertion Expected
expect_loadable yes
expect_valid_geometry no
expect_crs yes
expected_epsg 4326
expected_geometry_types Point

Known consumer divergences

Disagreements already investigated on this case. If your reader reproduces one of these, it is catalogued — not a new finding.

pyogrio — pyogrio >=0.11, GDAL 3.12-3.13 (originates in GDAL)

Under a spatial filter (bbox= or mask=), the Arrow path (use_arrow=True) returns the NULL-geometry row that the numpy path and GDAL's own ogrinfo -spat both exclude -- 3 rows against 2. Traced to GPKG's GetArrowStream fast path; only GPKG diverges among GeoJSON, Shapefile and SQLite. A NULL geometry intersects nothing, so the numpy path is the correct one.

Upstream: https://github.com/farzinashouri/geocase/blob/main/docs/geocase_validate/gdal-issue-draft.md

lonboard — lonboard 0.11, geoarrow-pyarrow 0.5

from_geopandas emits no Arrow validity bitmap, so a NULL geometry is indistinguishable from a geometry with NaN coordinates. geopandas' own Arrow export, which does set the bitmap, is the reference.

Upstream: https://github.com/farzinashouri/geocase/blob/main/docs/plans/38-six-consumer-round-2-and-the-stac-adapter.md

Notes

Purpose

This case tests the distinction between NULL and EMPTY geometries in GeoPackage format. This is a critical edge case that many loaders handle incorrectly.

Problem Demonstrated

GeoPackage (SQLite-based) distinguishes two "missing geometry" states:

State SQL Value WKB Representation Meaning
NULL NULL No value stored "No geometry recorded"
EMPTY WKB blob POINT EMPTY (7 bytes) "A point with no coordinates"

Why This Matters

Different operations behave differently with NULL vs EMPTY:

Operation NULL Geometry EMPTY Geometry
IS NULL check TRUE FALSE
Spatial index Not indexed May be indexed
ST_IsEmpty() NULL (or error) TRUE
Bounds calculation Excluded Zero-area bounds
Export to GeoJSON null or omitted {"type": "Point", "coordinates": []}

Test Data

This GeoPackage contains 4 rows:

Row ID Geometry State
1 valid_1 POINT(10 50) Valid geometry
2 valid_2 POINT(11 51) Valid geometry
3 null_row SQL NULL NULL geometry
4 empty_row POINT EMPTY EMPTY geometry

Expected Behavior

  • Loaders should successfully read all 4 rows
  • NULL and EMPTY geometries should be distinguishable
  • Spatial operations should handle both states correctly
  • Export should preserve the NULL/EMPTY distinction

Format-Specific Behavior

Format NULL Support EMPTY Support Notes
GeoPackage Yes (SQL NULL) Yes (WKB EMPTY) Full distinction
Shapefile No (all NULL) Sort of No true NULL, empty = deleted
GeoJSON Yes (null) Yes ([] coords) Spec allows both
WKT No Yes (POINT EMPTY) NULL not representable

This case is GeoPackage-specific because of SQLite's explicit NULL handling.

Required capabilities

  • load
  • geometry-validation
  • null-handling

Files

Browse this case on GitHub

Source and license

  • Source: geocase-curated
  • License: MIT

Tags

empty format_specific geometry_special geopackage null_handling vector