Skip to content
Novus Examples
5 min readNovus ExamplesGuideIntermediatev2026.09

Validate Geospatial Imports Without a GIS Stack

Antimeridian crossings, unnormalised longitudes, silent CRS assumptions, and a shapefile that is really five files. Test the cases that break importers.

Most geospatial bugs are not about geometry

They are about assumptions that held for every file the developer had until one arrived where they did not: that longitude is between -180 and 180, that a polygon does not cross the date line, that coordinates are latitude-longitude in that order, that a "shapefile" is a file.

The geospatial catalogue is 158 fixtures because each of those assumptions needs its own counter-example, and because the failure is almost never an exception. It is a shape drawn in the wrong place.

The antimeridian, which is where correctness goes to die

There are four fixtures here and they exist as two deliberate pairs:

  • geojson-antimeridian-linestring-unsplit and geojson-antimeridian-linestring-split
  • geojson-antimeridian-polygon-unsplit and geojson-antimeridian-polygon-split

A line from longitude 179 to -179 is two degrees long going east. Software that interpolates naively draws it as 358 degrees going west: a stripe straight across the entire map. The GeoJSON specification says to cut geometries at the antimeridian, which is what the split members do, and plenty of real data does not, which is what the unsplit members are.

Testing with only one of each pair proves nothing useful. What you want to know is whether your renderer draws the same picture for both, because they describe the same place.

geojson-longitude-unnormalised is the related case: coordinates outside the -180..180 range that are arithmetically meaningful and specification-invalid. geojson-pole-points covers the other degenerate location, where longitude stops being meaningful at all.

A shapefile is five files and will not tell you which is missing

shapefile-towns-shp, shapefile-towns-shx, shapefile-towns-dbf, shapefile-towns-prj and shapefile-towns-cpg are one dataset. The geometry is in .shp, the index in .shx, the attributes in .dbf, the coordinate reference system in .prj and the attribute encoding in .cpg.

Two failure modes follow, and both are quiet:

  • Without the .prj, most readers do not fail. They assume WGS84, and if the data is actually in a projected system your features land in the Gulf of Guinea, near zero-zero, or simply somewhere wrong by a consistent offset. A reader that guesses is worse than one that refuses.
  • Without the .cpg, attribute text is decoded with a guessed codepage, so names with accented characters come back mangled while the geometry is perfect.

Because these are published as separate downloadable fixtures rather than a bundle, you can test the missing-sidecar case directly: fetch four of the five and see what your importer says.

Coordinate reference systems, stated rather than assumed

The crs group is a set of .prj payloads on their own: prj-wgs84-wkt1, prj-wgs84-wkt2, prj-web-mercator-wkt1, prj-utm-28n-wkt1 and prj-equidistant-cylindrical-wkt1.

Two distinctions matter here. WKT1 and WKT2 are different serialisations of the same idea, and a parser that only handles WKT1 will meet WKT2 eventually. And Web Mercator versus WGS84 is the classic silent error: both are "the usual one" to different audiences, the numbers look plausible in both, and the difference shows up as features drifting further from truth the further you get from the equator.

gml32-crs84-lon-lat and gml21-legacy-coordinates capture the axis-order problem in its natural habitat. Older GML says latitude first; CRS84 says longitude first. Swapping them produces coordinates that are still valid numbers, which is why this one reaches production so often.

Precision, and how much of it is real

coordinate-precision-01dp through coordinate-precision-15dp are the same geometry written with one, two, four, six, nine and fifteen decimal places.

They answer two different questions. The first is a storage question: six decimal places is roughly ten centimetres, and everything beyond it is usually noise inherited from a float, so the higher-precision members are where you measure what rounding costs you in file size. The second is a parser question: fifteen decimal places will exercise how your reader handles a double it cannot represent exactly, and whether round-tripping changes the value.

Formats that arrive from devices and from the field

  • nmea-mixed-sentences, nmea-gga-only, nmea-no-fix, nmea-bad-checksum, nmea-southern-eastern-hemisphere and nmea-lf-line-endings. NMEA is what GPS hardware emits. nmea-no-fix is the one worth testing first: a receiver with no satellite lock still produces well-formed sentences containing no position, and code that reads them as zero will place your user off the coast of Africa. nmea-bad-checksum answers whether you validate at all.
  • gpx-track-single-segment, gpx-track-multi-segment, gpx-route, gpx-waypoints, gpx-1-1-schema and gpx-1-0-schema. A track, a route and a waypoint list are three different things that look similar, and importers routinely conflate them.
  • kml-point-placemarks through kml-nested-folders, including kml-styles-and-stylemap, where the question is whether you preserve styling or silently drop it.

TopoJSON, where the arcs are the point

topojson-quantized-1e4 and topojson-quantized-1e2 differ in how aggressively coordinates were snapped to a grid. topojson-shared-arcs is the format's whole reason to exist: neighbouring polygons reference the same boundary once instead of twice, which is why TopoJSON files are small and why a naive converter produces gaps between regions that should share an edge.

topojson-arc-reversal-broken is the deliberate defect. An arc referenced in reverse must be walked backwards; getting that wrong produces a polygon that self-intersects, and the symptom is a region rendered as a bow tie.

GeoPackage and tiles

geopackage-features, geopackage-attributes and geopackage-web-mercator are SQLite databases with a spatial schema, which means your reader needs to handle a real database file rather than a text parse. mbtiles-tileset and mbtiles-empty-tileset are the raster equivalent, and the empty one exists for the same reason zip-empty does: valid, legal, and frequently treated as an error.

Where to start

If you import user-supplied geospatial data and can only test six things, test these:

  1. shapefile-towns-* with the .prj deliberately withheld.
  2. Both antimeridian pairs, compared against each other rather than individually.
  3. gml21-legacy-coordinates for axis order.
  4. nmea-no-fix, if any of your data comes from a device.
  5. topojson-shared-arcs, if you convert TopoJSON at all.
  6. coordinate-precision-15dp for round-trip stability.

Every fixture is generated from a recorded specification with pinned bytes, so a defect you find once stays findable.

Continue this workflow

Try the workflow

Documentation and troubleshooting

Was this article helpful?

Found an error? Send a correction.