Mapping tools have to survive a genuinely awkward set of formats, and most sample data is either a country outline or someone's holiday GPX. Here the same small invented territory — a handful of towns, roads, parcels, and a lake — is published across the formats GIS pipelines actually exchange: GeoJSON and TopoJSON, Esri shapefile component sets (.shp/.shx/.dbf/.prj) and their zipped bundles, KML and KMZ, OGC GeoPackage, GML, and Well-Known Text. GPS coverage spans GPX tracks and routes plus raw NMEA sentence logs. Coordinate-system fixtures pair the same geometry in WGS 84 and a projected CRS so reprojection can be scored against a known answer, and edge cases cover antimeridian crossings, holes in polygons, self-intersections, and empty geometries.
Intentionally invalid: the five towns with their coordinates written latitude-first. RFC 7946 fixes the order as longitude then latitude, and this is the mistake that arrives via an EPSG-URN-ordered source such as the GML twin in the territory subcategory.
Intentionally invalid: Web Mercator metres inside a file that claims to be GeoJSON, with a legacy `crs` member as the only hint. RFC 7946 allows exactly one coordinate system, so this file is what a desktop GIS produces when someone exports a projected layer to GeoJSON without reprojecting it first.
A FeatureCollection carrying the `crs` member from the 2008 GeoJSON specification. RFC 7946 removed it and fixed the CRS as WGS 84, so a modern reader should treat this as an ignorable foreign member — but plenty of exporters still write it and some readers still obey it.
The UTM zone that actually contains the invented territory: zone 28N, central meridian 15 W. It is a definition fixture only — no projected coordinates are published for it here, because a transverse Mercator value is not something to take on trust from a fixture.
The projection every web map uses, defined as Esri's Mercator_Auxiliary_Sphere. It treats the ellipsoid as a sphere for the projection maths while keeping ellipsoidal latitudes, which is why it is a pseudo-Mercator and why it cannot represent latitudes beyond about 85.05 degrees.
The reference .prj for this category: WGS 84 geographic in Esri's WKT 1 dialect, which uses D_WGS_1984 and GCS_WGS_1984 in place of the OGC spellings and carries no AUTHORITY node. Everything else in this family is a variation on it.
The same CRS in WKT 2, which renames almost every keyword (GEOGCRS, ELLIPSOID, LENGTHUNIT) and — crucially — declares axis order explicitly with AXIS and ORDER. WKT 1 left that implicit, which is the root of most latitude/longitude swaps.
Plate carrée on a sphere of radius 6,378,137 m — the simplest projection with a closed form you can check by hand. That is exactly why it is here: it makes the reprojection pair in this subcategory verifiable with a calculator rather than a library.
Nine control points — the five towns plus Null Island, the equator, a high-latitude point and the Web Mercator latitude limit — with their coordinates in both projections. The two target projections share the x formula and differ only in y, which isolates a latitude-transform bug from a longitude one.
The five towns in EPSG:3857 — Web Mercator, the projection every slippy map renders in. All three files hold the same five places, so reprojecting any one of them and diffing against another scores your transform against a known answer rather than against another implementation of the same library.
The five towns in EPSG:4087 — World Equidistant Cylindrical, whose closed form can be checked by hand. All three files hold the same five places, so reprojecting any one of them and diffing against another scores your transform against a known answer rather than against another implementation of the same library.
The five towns in EPSG:4326 — the geographic source, in longitude/latitude degrees. All three files hold the same five places, so reprojecting any one of them and diffing against another scores your transform against a known answer rather than against another implementation of the same library.
A top-level Geometry object with no Feature or FeatureCollection around it. This is legal GeoJSON and is what PostGIS `ST_AsGeoJSON` returns, but a surprising number of importers only accept a FeatureCollection.
Three features that each carry their own bbox. Spatial indexes are often built from these declared boxes instead of the geometry, so a wrong or stale one produces a query that silently misses features.
The five towns with a four-element bounding box on the collection, ordered west, south, east, north as RFC 7946 requires. The values are the true extent of the contained points, so a reader that recomputes the box can be scored against the declared one.
One feature whose properties mix every JSON type and nest three levels deep. Formats with a flat attribute table — shapefile DBF, most SQL layers — cannot represent this, so the fixture shows whether a converter flattens, JSON-encodes, or discards the nested values.
A structurally valid FeatureCollection with an empty `features` array — what a spatial query legitimately returns when nothing matches. It is the standard fixture for the empty-state path of a map component.
RFC 7946 section 6 allows members it does not define to sit alongside the ones it does, and requires parsers to ignore rather than reject them. This collection carries three such members at the top level.
All seven RFC 7946 geometry types in a single FeatureCollection. Formats with a single-geometry-type-per-layer model — shapefile, most database tables — cannot hold this in one layer, so it is the fixture that reveals how a converter splits or refuses it.
Four features whose labels are written in Latin with diacritics, katakana, right-to-left Arabic, and a decomposed combining sequence — all stored as raw UTF-8 rather than \u escapes. Shapefile DBF export is where these usually turn into question marks.
One feature with `"properties": {}`, one with `"properties": null` and one fully populated. Both empty forms are legal, and attribute-table writers routinely fall over on the null form when they try to enumerate its keys.
The same five towns keyed by numeric ids. RFC 7946 allows an id to be either a string or a number, and code that assumes one type breaks on the other — usually by stringifying 1 into "1" and then failing to match on a later join.
One Feature at the top level, with properties and an id but no enclosing collection — the shape a single-record API response takes. Pair it with the bare-geometry fixture to find readers that only handle one of the three legal root types.
Five features whose `id` is a string and sits on the Feature itself rather than inside `properties` — the placement RFC 7946 specifies and the one most CSV-shaped importers get wrong by flattening it into an attribute.
RFC 7946 permits `"geometry": null` for a feature with attributes but no location. Readers that assume every feature has coordinates crash here, and writers that emit an empty Point instead corrupt the record's meaning.
A single Feature whose geometry is a GeometryCollection of a Point, a LineString and a Polygon. Many simple readers flatten or silently drop these, and many writers cannot produce them at all, so it is the usual place a conversion loses data.
A one-feature FeatureCollection holding the four-vertex Coast Road, so a reader can be exercised on LineString in isolation. Coordinates come from the Kestrel Bay territory, so the same shape appears again in every other format in this category.
A one-feature FeatureCollection holding all three roads as one geometry, so a reader can be exercised on MultiLineString in isolation. Coordinates come from the Kestrel Bay territory, so the same shape appears again in every other format in this category.
A one-feature FeatureCollection holding three harbour buoys, so a reader can be exercised on MultiPoint in isolation. Coordinates come from the Kestrel Bay territory, so the same shape appears again in every other format in this category.
A one-feature FeatureCollection holding the three parcels as one geometry, so a reader can be exercised on MultiPolygon in isolation. Coordinates come from the Kestrel Bay territory, so the same shape appears again in every other format in this category.
A one-feature FeatureCollection holding a single position, so a reader can be exercised on Point in isolation. Coordinates come from the Kestrel Bay territory, so the same shape appears again in every other format in this category.
A one-feature FeatureCollection holding a simple four-corner parcel, so a reader can be exercised on Polygon in isolation. Coordinates come from the Kestrel Bay territory, so the same shape appears again in every other format in this category.
A one-feature FeatureCollection holding a parcel with a courtyard cut out of it, so a reader can be exercised on Polygon in isolation. Coordinates come from the Kestrel Bay territory, so the same shape appears again in every other format in this category.
The same transect cut at the 180th meridian into a two-part MultiLineString, which is what RFC 7946 section 3.1.9 recommends. The split latitude is the linear interpolation of the unsplit twin, so the two files describe the same ground path.
The straddling block cut at 180 into two polygons that share an edge on the meridian — the interoperable form. Use it as the expected output when testing an antimeridian-splitting routine against the unsplit twin.
Both rings of the Mill Court parcel are wound the wrong way round: the exterior clockwise, the interior counter-clockwise. RFC 7946 mandates the right-hand rule, and consumers that rely on winding to tell holes from shells will read this polygon inside out.
A GeometryCollection with an empty `geometries` array — structurally valid, semantically the empty geometry. Converters that map a collection onto a multi-geometry type usually emit null here, quietly changing the record's meaning.
A feature carrying a LineString with an empty coordinate array. Empty geometries are what spatial operations legitimately return — an intersection that misses, a clip that removes everything — and index and rendering code routinely assumes at least one coordinate exists.
A feature carrying a Polygon with no rings at all. Empty geometries are what spatial operations legitimately return — an intersection that misses, a clip that removes everything — and index and rendering code routinely assumes at least one coordinate exists.
Intentionally invalid: the file is perfectly good JSON but the coordinates are quoted strings, which RFC 7946 does not allow. It is exactly what a CSV-to-GeoJSON script produces when it forgets to cast, and a permissive reader will coerce it and hide the bug.
Intentionally invalid: the second ring is a whole separate parcel filed as an interior ring of the first, which OGC Simple Features forbids. It is what happens when a MultiPolygon is flattened into a Polygon by concatenating rings.
Intentionally invalid: latitude 95, five degrees past the north pole. This is the value that appears when longitude and latitude have been swapped somewhere upstream, so catching it early usually finds an axis-order bug rather than a data-entry one.
Intentionally invalid: a point at longitude 200. It is a plausible-looking number that usually comes from adding an offset without wrapping, and it survives JSON schema checks that only test for `number`.
Intentionally invalid: a ring of three positions that closes back on itself and therefore encloses nothing. RFC 7946 requires at least four positions for a linear ring, and this is the shape a simplification pass produces when it over-thins a small polygon.
Intentionally invalid: a bowtie ring whose two lobes wind in opposite directions, so the shoelace areas nearly cancel. Buffer, intersection and area operations on this geometry return nonsense rather than failing, which is why it belongs in a validation suite.
Intentionally invalid: the parcel's exterior ring omits the repeated closing position that RFC 7946 requires. Many readers close it silently, which hides the producer's bug until a stricter consumer downstream rejects the data.
A two-point line whose longitudes step from 179.6 to -179.7. Interpolating naively draws a 359.3-degree line all the way back around the world instead of the 0.7-degree hop across the 180th meridian — the classic antimeridian artefact.
A flight path that goes up one meridian, touches the pole, and comes down the antipodal meridian — so longitude jumps by exactly 180 degrees at the middle vertex. Renderers that interpolate in longitude draw a horizontal streak across the whole map instead.
The same transect with its eastern end written as 180.3 rather than the normalised -179.7. Producers that accumulate longitudes across the antimeridian emit this constantly; RFC 7946 restricts longitude to -180..180, so a strict reader must reject or normalise it.
Three points at exactly plus or minus 90 degrees latitude, two of which are the same physical place written with different longitudes. Web Mercator cannot represent either pole — latitude 90 projects to infinity — so this is the fixture that catches divide-by-zero and NaN in a reprojection path.
A ring that encloses the north pole by running along 85 N and closing across the top of the graticule. Planar area and centroid routines return meaningless answers here, and many renderers draw a rectangle rather than a cap.
A rectangle whose east edge is at 179.7 W and west edge at 179.6 E. Read literally in planar coordinates it wraps the entire globe and inverts the inside/outside test, so point-in-polygon queries return the exact opposite of the truth.
Four positions with a third element, which RFC 7946 defines as height in metres above the WGS 84 ellipsoid. Two-dimensional pipelines commonly truncate the third element without saying so, and this file is what proves whether yours does.
A GeoPackage holding an `attributes` table with no geometry at all — the standard's own answer to a lookup table you want to ship alongside your layers. Its gpkg_contents row has null bounds and a null SRS, which is what readers that assume every row is spatial trip over.
The same five towns stored as projected metres rather than degrees, with EPSG:3857 registered in gpkg_spatial_ref_sys and stamped into every geometry blob's header. Unlike GeoJSON, GeoPackage genuinely supports this — so it is the clean way to test that your reader honours the declared SRS instead of assuming 4326.
The territory as an OGC GeoPackage: an ordinary SQLite database with the three required metadata tables and three feature layers whose geometry columns hold GeoPackage binary blobs. Unlike a shapefile it carries several geometry types, long column names and real typed columns in one file.
A small raster tile pyramid covering the invented territory, one flat colour per zoom level so a wrongly addressed tile is obvious on sight. The row axis follows the TMS convention, which is flipped relative to the XYZ scheme web maps use — the single most common reason an MBTiles layer renders upside down.
Every required MBTiles table and metadata key, and not one tile row. This is what a failed or interrupted tile build actually produces, and it is the fixture that finds code which reports success because the file opened and the schema validated.
GML 2.1 packed coordinates into a single `gml:coordinates` string whose decimal, coordinate and tuple separators are all declared as attributes and can be anything. GML 3 replaced it with `gml:posList`, but WFS 1.0 servers still emit this, and a parser that assumes the default separators mis-reads any file that changes them.
The same five towns as the territory GML twin, but declared with the CRS84 URN instead of the EPSG one — which flips the axis order to longitude first. The two files are otherwise identical in structure, so together they isolate axis-order handling from everything else.
GML calls a multipolygon a MultiSurface and its parts surfaceMembers, and nests exterior and interior rings under named elements rather than relying on ring order. This feature holds the holed lake and a solid parcel in one geometry, which is the shape a WFS response usually takes.
Intentionally invalid: a way referencing node -9 and a relation referencing way -999, neither of which is in the file. This is the normal state of a bounding-box extract, where objects are clipped at the boundary — so a reader has to decide between rejecting the file and building partial geometry, and must say which.
Wither Lake and its island as an OSM multipolygon relation: two closed ways tagged separately and joined by a relation whose members carry outer and inner roles. OSM has no polygon primitive at all, so reconstructing the hole means reading the relation rather than the ways.
Eight trackpoints each carrying a Garmin TrackPointExtension with heart rate, cadence and air temperature. The GPX schema deliberately leaves `extensions` open, so this is the fixture for checking that a reader surfaces vendor data instead of discarding it on export.
Seven trackpoints whose timestamps go forwards, backwards, and then repeat — the pattern a receiver produces after a clock correction or a buffered replay. Naive speed calculations divide by a negative or zero time delta and emit impossible values.
A `rte` element: an ordered list of waypoints describing a planned route, with no timestamps because it has not been travelled yet. Many importers only look for `trk` and report this perfectly valid file as empty.
The baseline GPS trace for this category: 24 timestamped, elevated trackpoints in a single segment, walking the Coast Road of the invented territory. Every other GPX fixture here is this file with one thing changed.
Five `wpt` elements for the towns of the territory, with names, symbols and descriptions but no track or route. It is the GPX equivalent of a point layer, and the third of the three top-level GPX content types.
The same eight points timestamped with a +01:00 offset rather than the usual Z. The digits are identical to the reference track, so a parser that strips the offset and assumes UTC produces times that are wrong by exactly one hour — a silent error that only shows up when two traces are merged.
The same 24 points split into two `trkseg` elements with a ten-minute gap between them — what a receiver writes after a stop or a signal loss. Tools that concatenate segments without noticing the boundary report a moving average dragged down by a pause that never happened on the move.
A track containing exactly one point, which is what a receiver writes when a recording is stopped immediately. Distance, speed and bearing all need a pair of points, so this is the fixture that finds off-by-one loops and empty-slice crashes.
Eight trackpoints with timestamps but no `ele` element, the shape a receiver writes on a 2D fix. Elevation-profile code that assumes the element is present either crashes or charts a flat line at zero, which looks like real data.
The same eight points with elevation but no `time` element — what you get when a track is drawn in a planning tool rather than recorded. Speed and pace calculations must refuse to produce a number here instead of dividing by a zero duration.
The same eight trackpoints written to the GPX 1.0 schema. The two versions differ in namespace and in where document metadata lives — 1.1 wraps it in a `metadata` element, 1.0 puts name, desc and time directly under `gpx` — which is exactly what a namespace-hardcoding reader gets wrong.
The same eight trackpoints written to the GPX 1.1 schema. The two versions differ in namespace and in where document metadata lives — 1.1 wraps it in a `metadata` element, 1.0 puts name, desc and time directly under `gpx` — which is exactly what a namespace-hardcoding reader gets wrong.
Attributes carried in `ExtendedData/Data` elements, which have names and display names but no declared types — every value is a string. Importers must infer that population is a number, and this is where a silently wrong inference starts.
Four extruded points whose third coordinate means three different things depending on the `altitudeMode` beside it: ignored when clamped, height above terrain when relative, height above the ellipsoid when absolute. Copying the number without the mode changes what the data says.
A `gx:Track` from the Google KML extension: ten `when` timestamps followed by ten `gx:coord` positions, paired by index rather than nested. Note the coordinates are space-separated here even though core KML uses commas — a detail that breaks parsers reusing the same splitting code.
A georeferenced GroundOverlay pinned to the territory's bounding box and a ScreenOverlay anchored to a pixel position. Both reference images that are not present, which is the normal state for a bare .kml and the reason KMZ exists.
The three roads as LineString Placemarks with `tessellate` set, which tells a globe renderer to drape the line over terrain rather than drawing it as a straight chord. Converters that drop the flag produce lines that tunnel through hills.
One Placemark whose MultiGeometry mixes three Points with a LineString. GeoJSON has no equivalent single type for a heterogeneous set, so this must become a GeometryCollection — and converters that reach for MultiPoint instead lose the line.
Eight Placemarks arranged in three levels of Folder, the hierarchy KML uses for layer trees. GeoJSON has nothing equivalent, so a converter must either flatten and record the path or lose the structure entirely — and this file makes which one it did obvious.
Five town Placemarks with plain-text descriptions and no styling — the smallest useful KML and the baseline for the rest of this subcategory. Coordinates match the master territory, so a KML-to-GeoJSON conversion can be diffed against a known answer.
Two polygons that each carry an `innerBoundaryIs` ring: a parcel with a courtyard and a lake with an island. KML names its rings explicitly instead of relying on winding order, so converting to GeoJSON means deciding the winding rather than copying it.
Shared `Style` blocks referenced by `styleUrl`, plus a `StyleMap` pairing normal and highlight states. KML colours are written aabbggrr rather than the usual rrggbbaa, which is why converted maps so often come out with red and blue swapped.
Four Placemarks with a `TimeSpan` and one with an instantaneous `TimeStamp`, which is what drives the time slider in a globe viewer. Time is the first thing a KML-to-GeoJSON conversion drops, because RFC 7946 has no place to put it.
The same five towns with a declared `Schema` and typed `SimpleData` values — KML's answer to untyped ExtendedData. Pair it with the untyped fixture to check that your importer prefers a declared type over a guessed one.
A KMZ whose style points at `files/markers/town.png` inside the same archive. Resolving that relative href against the archive rather than the filesystem is the whole job of a KMZ reader, and it is where extract-then-parse implementations break.
A KMZ that contains a second KML in a subdirectory, reached through a `NetworkLink` with a relative href, plus an overlay image two directories deep. Readers that only load `doc.kml` show the overlay but none of the three parcels.
The minimal KMZ: a ZIP holding exactly one `doc.kml` at the archive root and nothing else. Readers are supposed to open the first .kml they find, and most look for the conventional name — this file satisfies both rules.
A log with one sentence whose checksum has been altered and a final sentence truncated mid-transmission with no checksum at all — the two ways a serial GPS feed actually degrades. A correct parser drops both and keeps everything else.
The same twelve fixes reduced to GGA alone. GGA carries time-of-day but no date and no speed, so a parser that only reads GGA cannot place the track on a calendar — a real limitation that this file makes obvious rather than leaving to be discovered later.
The same sentences terminated with a bare LF rather than the CR LF the standard mandates — what you get after a log passes through a text editor or a Unix pipeline. Parsers that strip only "\r\n" leave a stray character on every line and then fail the checksum.
A twelve-fix receiver log with the five sentence types a GPS unit normally interleaves, including three-part GSV satellite-in-view sets. Every checksum is correct, so a parser that verifies them should accept the whole file.
Six cycles of a receiver that has power but no satellite lock: GGA fix quality 0, RMC status V, and empty coordinate fields throughout. Parsers that split on commas and index positionally read these empty fields as zeros and plot the track at Null Island.
NMEA never writes a negative coordinate: sign lives entirely in the N/S and E/W letters, and the numeric field is always positive. This log sits in the southern and eastern hemispheres, so a parser that ignores the hemisphere letter puts every fix in the wrong quadrant of the world.
Real DBF component from the five-town Kestrel Bay shapefile set, copied byte-for-byte into the Convert v2 reserve. Stable P8 artifact p8-convert-shapefile-dbf.
GeoJSON semantic target for the five-town shapefile set, preserving point order, CODE/NAME/POP/FOUNDED attributes, and longitude/latitude coordinates; the omitted .cpg is safe because every copied DBF value is ASCII. Stable P8 artifact p8-convert-shapefile-geojson.
Real PRJ component from the five-town Kestrel Bay shapefile set, copied byte-for-byte into the Convert v2 reserve. Stable P8 artifact p8-convert-shapefile-prj.
Real SHP component from the five-town Kestrel Bay shapefile set, copied byte-for-byte into the Convert v2 reserve. Stable P8 artifact p8-convert-shapefile-shp.
Real SHX component from the five-town Kestrel Bay shapefile set, copied byte-for-byte into the Convert v2 reserve. Stable P8 artifact p8-convert-shapefile-shx.
The same harbour light written to 1 decimal place — enough to identify which country you are in. The ladder exists because coordinate precision is almost always chosen by accident, and shipping six unnecessary digits multiplies the size of every geometry in a tileset.
The same harbour light written to 15 decimal places — enough to identify beyond what a float64 can hold for a coordinate at all. The ladder exists because coordinate precision is almost always chosen by accident, and shipping six unnecessary digits multiplies the size of every geometry in a tileset.
The same harbour light written to 2 decimal places — enough to identify which town you are in. The ladder exists because coordinate precision is almost always chosen by accident, and shipping six unnecessary digits multiplies the size of every geometry in a tileset.
The same harbour light written to 4 decimal places — enough to identify which building you are in. The ladder exists because coordinate precision is almost always chosen by accident, and shipping six unnecessary digits multiplies the size of every geometry in a tileset.
The same harbour light written to 6 decimal places — enough to identify which doorway you are standing in. The ladder exists because coordinate precision is almost always chosen by accident, and shipping six unnecessary digits multiplies the size of every geometry in a tileset.
The same harbour light written to 9 decimal places — enough to identify far finer than any consumer receiver can fix. The ladder exists because coordinate precision is almost always chosen by accident, and shipping six unnecessary digits multiplies the size of every geometry in a tileset.
What each decimal place is actually worth on the ground, computed on a sphere of mean Earth radius at the territory's latitude. Longitude degrees are shorter than latitude degrees by the cosine of the latitude, which is why the two columns diverge.
Five features whose coordinates are the values that break naive JSON number handling: 17-significant-digit doubles, the classic 0.1 + 0.2 artefact, the largest float64 below 180, a 1e-7 offset, and coordinates written in exponent form. Re-serialising this file is where a parser quietly loses or gains a digit.
A one-line sidecar naming the encoding of the .dbf. Without it a reader has to guess — usually cp1252 — and every non-ASCII attribute value comes back as mojibake, which is the single most common shapefile data-loss bug.
The parcel attribute table, joined to the .shp purely by record order — there is no key and no explicit link. Deleting a geometry without deleting its row shifts every subsequent attribute onto the wrong shape, silently.
Three Polygon records, one of which has two rings: the Mill Court parcel and its courtyard. Shapefile winds rings the opposite way to GeoJSON — exterior clockwise, holes anticlockwise — and it has no explicit hole marker, so winding is the only signal a reader has.
The parcel index, whose second entry is markedly longer than the others because that record carries two rings. Use it together with the roads index to check that seek logic handles multi-part records as well as multi-record files.
The one-line WKT that tells a reader what the .shp coordinates mean. Esri's flavour uses D_WGS_1984 and GCS_WGS_1984 rather than the OGC names and carries no EPSG authority code, so identifying it means matching the datum and spheroid rather than reading an ID.
The road attribute table, including a numeric field declared with two decimal places. DBF stores numbers as right-justified ASCII, so the declared width and decimal count are the only thing that says 9.40 is not 940.
Three single-part PolyLine records, each carrying its own bounding box, a part index and a run of little-endian coordinate doubles. PolyLine and Polygon share this layout, which is why the shape-type byte in the header is the only thing that distinguishes an open line from a closed ring.
Unlike the point index, these entries have different lengths because each PolyLine holds a different number of vertices. That makes it the useful case for testing seek logic — a reader that assumes a fixed stride reads the first record correctly and then garbage.
The attribute table as a dBase III file: fixed-width, space-padded, ASCII numerics. Field names are capped at ten characters and there is no type for dates-with-time or for nested values, which is why shapefile export loses so much of a modern schema.
The geometry half of a point shapefile: a 100-byte header carrying the shape type and bounding box, then five 20-byte Point records. The file is useless on its own — attributes live in the .dbf and the coordinate system in the .prj, which is the point of shipping the whole component set.
The index that lets a reader seek directly to record N of the .shp instead of walking the file. Its header duplicates the .shp header exactly, and a mismatch between the two is the most common way a hand-edited shapefile goes wrong.
A zipped shapefile containing a three-file polygon set with no .cpg, so the DBF encoding is undeclared. Zipping is how shapefiles are actually exchanged, because a bare .shp is meaningless without its sidecars — and it is the upload format most web GIS tools accept.
A zipped shapefile containing the full five-file point set — .shp, .shx, .dbf, .prj and .cpg. Zipping is how shapefiles are actually exchanged, because a bare .shp is meaningless without its sidecars — and it is the upload format most web GIS tools accept.
The sidecar that declares `attributes-cp1252.dbf` as ISO-8859-1. Note that the DBF also carries a language-driver byte in its header, and the two can disagree — which is exactly the ambiguity a reader has to resolve and document.
The sidecar that declares `attributes-utf8.dbf` as UTF-8. Note that the DBF also carries a language-driver byte in its header, and the two can disagree — which is exactly the ambiguity a reader has to resolve and document.
The same four accented attribute rows written in CP1252. Both files are exactly the same size because DBF fields are fixed-width in bytes, not characters — so the UTF-8 table fits fewer characters per field, and a reader that guesses the encoding produces mojibake rather than an obvious failure.
The same four accented attribute rows written in UTF-8. Both files are exactly the same size because DBF fields are fixed-width in bytes, not characters — so the UTF-8 table fits fewer characters per field, and a reader that guesses the encoding produces mojibake rather than an obvious failure.
Attributes plus a quoted WKT geometry column — the shape PostGIS `COPY`, DuckDB spatial and QGIS delimited-text imports expect. The geometry field contains commas, so it exercises quoted-field parsing as well as geometry decoding.
The territory as GML 3.2 with an EPSG URN srsName — which means latitude comes first, the opposite of GeoJSON. This is the single most common cause of features landing in the wrong hemisphere, so the file is built to catch it.
The same 14 features as KML Placemarks, with the multi-point buoys expressed as a MultiGeometry and the two holed polygons using innerBoundaryIs. KML orders coordinates longitude-first like GeoJSON, which makes it the least surprising twin in this group.
The territory as one GeoJSON Feature per line, the shape streaming pipelines and BigQuery/Athena loaders expect. There is no enclosing FeatureCollection, so a reader that insists on one will reject it.
The master dataset for this category: five towns, three roads, three parcels (one with a courtyard hole), a lake with an island, a buoy MultiPoint and the county boundary, as an RFC 7946 FeatureCollection. Kestrel Bay County is invented: open ocean at 47 N / 12.5 W, matching no real place, settlement, road or parcel.
The same 14 features with every optional byte of whitespace removed. Pair it with the pretty-printed master to test that a parser's output is whitespace-independent and that a formatter round-trips without reordering members.
Every geometry in the territory as a bare WKT string, one per line and in master order. Attributes are deliberately absent so the file isolates geometry serialisation from attribute mapping.
The towns and roads in OSM XML: tagged nodes, three highway ways sharing the junction node at Kestrel Bay, and a route relation over them. All object IDs are negative — the convention for objects that have never been uploaded — so nothing here can be mistaken for real OSM data.
The territory as TopoJSON with no quantization and no transform, so arc coordinates are the raw WGS 84 positions. Decoding it must reproduce the master GeoJSON exactly — which makes it the reference case before the quantized fixtures introduce rounding.
The five towns as a plain CSV with separate longitude and latitude columns — the most common way point data actually arrives. Convert it to GeoJSON and diff against the Point features in the master to score column-order and axis-order handling.
The same territory quantized to only 100 steps, so rounding error reaches roughly 220 m and several vertices collapse onto the same grid cell. Use it to prove that a tolerance in your comparison code is actually being applied rather than accidentally passing.
Exactly what the shared-arc topology decodes to: two polygons whose rings are stitched from arcs 0, 1 and 2 with arc 0 reversed for the eastern parcel. Because that file is unquantized, this comparison is exact rather than tolerance-based.
Five town geometries that each carry both an `id` member and a `properties` object. TopoJSON keeps these separate, and converters that fold the id into properties (or drop it) break joins against an external attribute table.
Intentionally invalid: East Field references the shared arc forwards when it should use the reversed form, so its ring no longer closes. This is the single most common hand-editing mistake in TopoJSON, and it produces a plausible-looking file that renders as a torn shape.
The full territory quantized onto a 10,000-step grid with a transform block and delta-encoded arc positions — the default TopoJSON encoding. Rounding is bounded by the grid spacing, which over this 0.28-degree extent is about 2.2 m.
One topology carrying three separately named objects — towns, roads and areas — that all index into the same arc pool. This is how a single TopoJSON file ships what would otherwise be three layers, and readers that assume exactly one object silently show only the first.
Two adjacent parcels that share their common boundary as a single arc: West Field references it as 0 and East Field as ~0, the negative-index form that means traverse it backwards. Storing the boundary once is the whole point of TopoJSON, and it is also where naive decoders produce gaps or slivers.
A topology whose fourth arc repeats the same position twice, giving a line of zero length. Quantization produces these routinely when two vertices land in the same grid cell, and simplification code that divides by segment length fails on them.
PostGIS EWKB, where the SRID is carried by OR-ing 0x20000000 into the geometry-type word and appending the code. A strict OGC WKB reader sees type 0x20000001 instead of 1 and either fails or, worse, reports an unknown geometry type.
PostGIS's extended WKT, which prefixes the geometry with `SRID=4326;`. It is not part of the OGC standard, so a strict WKT parser rejects the whole string — while a PostGIS-aware one gets the coordinate system for free.
The same point encoded both ways, plus a polygon, as uppercase hex — the form WKB takes when it travels through a text column or a SQL console. The byte-order flag is the first byte of each string, so the two point encodings differ everywhere after it.
Twenty-one raw bytes: a byte-order flag, a little-endian geometry-type code of 1, and two float64 ordinates. This is the wire format PostGIS, GeoPackage and most spatial drivers actually move geometry in.
One EMPTY value for each WKT geometry type, one per line. Empty geometries are what spatial operations return when nothing is left, and round-tripping them is where a database driver usually turns a typed empty into a plain NULL.
A single WKT value for a point, a line and a polygon in one value. WKT nests with bare parentheses and no type tags on the inner parts, so the number and depth of brackets is the only thing that distinguishes a polygon ring from a multipolygon member — which is where hand-written parsers go wrong.
Intentionally invalid: four WKT strings that each break in a different way — a point with one ordinate, a line whose last vertex is ragged, a polygon missing a closing bracket, and a misspelled keyword. Good parsers report which line and which character failed.
A single WKT value for the four-vertex Coast Road. WKT nests with bare parentheses and no type tags on the inner parts, so the number and depth of brackets is the only thing that distinguishes a polygon ring from a multipolygon member — which is where hand-written parsers go wrong.
A point with an M (measure) ordinate rather than a Z. Written without the tag this is indistinguishable from a Z geometry, and a parser that assumes the third ordinate is elevation silently converts a 345-second measure into a 345-metre altitude.
A single WKT value for all three parcels in one geometry. WKT nests with bare parentheses and no type tags on the inner parts, so the number and depth of brackets is the only thing that distinguishes a polygon ring from a multipolygon member — which is where hand-written parsers go wrong.
A single WKT value for the Kestrel Bay town centre. WKT nests with bare parentheses and no type tags on the inner parts, so the number and depth of brackets is the only thing that distinguishes a polygon ring from a multipolygon member — which is where hand-written parsers go wrong.
A single WKT value for the Mill Court parcel and its courtyard. WKT nests with bare parentheses and no type tags on the inner parts, so the number and depth of brackets is the only thing that distinguishes a polygon ring from a multipolygon member — which is where hand-written parsers go wrong.
A point with an explicit Z dimension tag. Some producers write `POINT Z (x y z)` and others write `POINT(x y z)` with no tag at all, so a parser has to cope with the dimension being declared or merely implied by the ordinate count.
A three-vertex line carrying both elevation and a measure, so each position has four ordinates. GeoJSON tops out at three, which makes this the fixture that shows what a WKT-to-GeoJSON conversion has to drop and whether it says so.
Yes. One small invented territory is published as GeoJSON, TopoJSON, shapefile component sets, KML/KMZ, GeoPackage, GML, and WKT, so conversion fidelity can be measured against a known shape.
How do I test reprojection?+
Coordinate-system fixtures pair the same features in WGS 84 and a projected CRS, with the transform documented in specs — reproject one and diff against the other.
What geometry edge cases are covered?+
Antimeridian crossings, polygons with holes, self-intersecting rings, empty and null geometries, and mixed-type feature collections, each labelled with what it is meant to break.
We use Google Analytics and show ads via Adsterra. Non-essential cookies and ad scripts run only after you allow the matching categories. See our cookie policy.