Choosing a Coordinate Quantisation Grid for Delta Encoding
One number decides whether your deltas record edits or record floating-point noise, and it is usually chosen by copying whatever the last project used. This page is a focused companion to delta tracking algorithms for vector data.
Concept & Context
A coordinate stored as a double carries about fifteen significant digits. A survey captured with survey-grade GNSS is accurate to two or three centimetres. Everything between those two is arithmetic: the residue of a reprojection, a repeated transformation, or a library that rounds differently from the one that wrote the file.
Delta encoding compares coordinates, so it sees that residue as change. Without quantisation, a repository can record a full delta for a layer that nobody edited, purely because it passed through a reprojection. With quantisation set correctly, the same operation produces no delta at all.
The grid has a floor and a ceiling. It must be coarser than the capture noise β otherwise noise survives quantisation β and finer than the smallest edit that matters, or real work disappears. Between those two bounds the choice is mostly free, and the useful convention is to sit an order of magnitude below the survey accuracy: 1 mm for a 2β3 cm survey, 1 cm for a decimetre-accurate municipal layer, 10 cm for a display-only basemap.
Core Algorithmic Pipeline
- Establish the survey accuracy from the capture metadata, not from the coordinate precision in the file.
- Set a candidate grid an order of magnitude finer than that accuracy.
- Measure the phantom rate: round-trip a copy of the data through the pipeline without editing it, and count features reported as changed.
- Measure the miss rate: apply known edits at the smallest size that matters and confirm every one is detected.
- Record the grid in the delta header and in the layer metadata, so the number travels with the data.
Working Implementation
"""Choose and verify a quantisation grid by measuring both failure directions."""
from __future__ import annotations
import geopandas as gpd
import numpy as np
from shapely import set_precision
from shapely.ops import transform
def quantise(gdf: gpd.GeoDataFrame, grid: float) -> gpd.GeoDataFrame:
"""Snap every coordinate onto the grid, in CRS units."""
if gdf.crs is None or gdf.crs.is_geographic:
raise ValueError(
"quantisation must be applied in a projected CRS β a grid expressed "
"in degrees is a different distance at every latitude"
)
out = gdf.copy()
out["geometry"] = out.geometry.apply(lambda g: set_precision(g, grid))
return out
def phantom_rate(gdf: gpd.GeoDataFrame, grid: float, epsg_via: int = 4326) -> float:
"""Fraction of features that a round trip makes look changed at this grid.
The round trip is a stand-in for the ordinary operations a layer survives in
a real pipeline β a reprojection out and back, which changes nothing that
was measured and perturbs every digit that was not.
"""
original = quantise(gdf, grid)
round_tripped = quantise(
gdf.to_crs(epsg=epsg_via).to_crs(gdf.crs), grid
)
same = [
a.equals(b) for a, b in zip(original.geometry, round_tripped.geometry)
]
return 1.0 - (sum(same) / len(same))
def miss_rate(gdf: gpd.GeoDataFrame, grid: float, edit_m: float,
sample: int = 500, seed: int = 12345) -> float:
"""Fraction of deliberate edits of size `edit_m` that this grid hides."""
rng = np.random.default_rng(seed)
idx = rng.choice(len(gdf), size=min(sample, len(gdf)), replace=False)
before = quantise(gdf.iloc[idx], grid)
shifted = gdf.iloc[idx].copy()
shifted["geometry"] = shifted.geometry.apply(
lambda g: transform(lambda x, y, z=None: (x + edit_m, y), g)
)
after = quantise(shifted, grid)
hidden = [a.equals(b) for a, b in zip(before.geometry, after.geometry)]
return sum(hidden) / len(hidden)
def evaluate(path: str, candidates=(0.0001, 0.001, 0.01, 0.1),
smallest_real_edit_m: float = 0.05) -> None:
gdf = gpd.read_file(path)
print(f"{'grid (m)':>10} {'phantom %':>10} {'missed %':>9} verdict")
for grid in candidates:
p = phantom_rate(gdf, grid) * 100
m = miss_rate(gdf, grid, smallest_real_edit_m) * 100
if m > 0:
verdict = "too coarse β hides real edits"
elif p > 1:
verdict = "too fine β records noise"
else:
verdict = "usable"
print(f"{grid:>10.4f} {p:>10.2f} {m:>9.2f} {verdict}")
if __name__ == "__main__":
evaluate("data/parcels.gpkg", smallest_real_edit_m=0.05)
A representative run on a parcel layer captured to 3 cm:
grid (m) phantom % missed % verdict
0.0001 37.40 0.00 too fine β records noise
0.0010 0.12 0.00 usable
0.0100 0.00 0.00 usable
0.1000 0.00 91.80 too coarse β hides real edits
Both usable rows are defensible; 1 mm leaves more headroom if the layer is later resurveyed with better equipment, and costs almost nothing in delta size relative to 1 cm.
Validation & Output Verification
# The chosen grid must produce a clean no-op on unchanged data
import geopandas as gpd
from quantise import quantise, phantom_rate
GRID = 0.001
gdf = gpd.read_file("data/parcels.gpkg")
assert phantom_rate(gdf, GRID) < 0.01, "the pipeline records change where none exists"
# And it must be recorded where the delta consumer will find it
import json
delta = json.load(open("deltas/parcels_2026-08-06.json"))
assert delta["quantisation_grid_m"] == GRID
assert delta["crs"] == gdf.crs.to_string()
print("grid recorded with the delta")
# A grid change is a migration, so make it visible in review
git log -p --follow -- params.yaml | grep -n "quantisation_grid" | head
If the grid appears in a commit alongside data changes, the review cannot separate the two β the diff will show nearly every feature as modified and nobody can tell which of those were real.
Failure Modes
-
A delta for a layer nobody edited β symptom: a full-size delta after a reprojection. Root cause: grid finer than the arithmetic noise. Fix: measure the phantom rate and move one order of magnitude coarser.
-
An edit that never reaches history β symptom: the working file differs from the last committed state and the delta is empty. Root cause: grid coarser than the edit. Fix: measure the miss rate against the smallest edit that matters; this failure is silent, so the test is the only signal.
-
Grid applied in a geographic CRS β symptom: precision varies by latitude across a national layer. Root cause: a grid expressed in degrees. Fix: quantise in a projected CRS, as the implementation enforces.
-
Two deltas cannot be compared β symptom: replaying a delta produces different geometry than expected. Root cause: the deltas were produced under different grids and neither recorded it. Fix: write the grid into the delta header and refuse to apply a delta whose grid differs from the target layerβs.
Related
- Delta Tracking Algorithms for Vector Data β the parent guide and the pipeline this parameter governs
- Hashing Spatial Datasets for Reproducible Fingerprints β the same decision, applied to fingerprints instead of deltas
- Spatial Diff Algorithms for Polygon Data β precision snapping as it appears in the diff pipeline
- Tuning Tolerance Thresholds for Conflict Detection β the related threshold on the merge side