The usual reason to want a JSON Schema for GeoJSON is to stop a language model writing broken geometry. Most hosted model APIs accept a schema and guarantee the output matches it, which sounds like it should end the problem. It does not, and this page is an attempt to say precisely how far it gets you.
The schema below was run over 61 small GeoJSON files on . Each one isolates a single edge case and records the clause of RFC 7946 it comes from, so a result says which rule was tested and not merely that something failed. The corpus and its manifest are on the datasets page.
- geojson.schema.json, draft 2020-12, the specification as written.
- geojson.strict.schema.json, the same thing reshaped for constrained decoding, which is the trick that makes a model emit only text matching a schema.
Both are published under CC BY 4.0. Use them, and see the data page for how to cite them.
What it gets right
Of the 61 fixtures, the schema refuses 24 and accepts 37. The number that matters more: it refuses none of the 20 documents RFC 7946 calls valid. A schema that rejected correct data would be worse than no schema, because the failure would look like a data problem when the tooling is at fault.
Where it is stricter than the specification
One rule in the schema is a choice of ours, and it is flagged in the file itself. A position may carry at most three elements: longitude, latitude and height.
The specification is softer than that. It says implementations should not go beyond three elements, and that a parser may ignore any extra ones. So a fourth element, usually a timestamp or a linear measure, is discouraged and still legal, and a schema claiming to encode only what RFC 7946 requires would let it through.
It is refused here because the fourth element has no defined meaning and almost nothing downstream reads it correctly. If you need the spec exactly, delete maxItems from the position definition. Either way it is worth knowing which of the two you have.
Worth noting alongside it: this site's own validator accepts that four-element position, so on this one fixture the schema is stricter than we are.
The five rules that get past it
These are the cases where RFC 7946 states a requirement and the schema accepts the document anyway. Two of them are beyond any schema, because they compare one value in the document against another. The other three are coordinate bounds that draft 2020-12 can state and this file leaves open, which is a choice rather than a limit.
| Rule | Why it gets through |
|---|---|
| A ring must start and end at the same position | Beyond any schema. The check compares two values inside one array, and JSON Schema has no way to say that one element equals another. |
| Longitude runs from -180 to 180 | A bound this schema does not set. prefixItems can cap the first element of a position at 180 while a third element still passes for height; measured against this corpus it refuses nothing valid. |
| Latitude runs from -90 to 90 | The same choice in the second element. |
| Coordinates are degrees in WGS 84 | Units are not a property of the JSON. Nothing says whether -8236050 is a mistake or a projection, though a longitude bound would refuse this one on its size alone. |
| A hole sits inside its exterior ring | Beyond any schema. Answering it means intersecting two rings, which is geometry. |
So a model constrained by this schema will still produce polygons that do not close and longitudes past 180. It will produce them in well-formed GeoJSON, which makes them harder to notice, and a schema is the wrong place to look for the catch. Validate the output.
What the strict variant costs
Constrained decoding usually asks for two things the specification does not: every object closed to members it did not declare, and every property listed as required. The strict schema applies both, and the difference between the two files is measured.
Of the 61 fixtures, the canonical schema accepts 37 and the strict one accepts 34. three documents are lost:
| Fixture | RFC 7946 | Why the strict schema refuses it |
|---|---|---|
foreign-member | valid | the member "title" is not allowed once the object is closed |
legacy-crs-named | ambiguous | the member "crs" is not allowed once the object is closed |
legacy-crs-projected | ambiguous | the member "crs" is not allowed once the object is closed |
One of those three is a real loss. RFC 7946 section 6.1 permits foreign members, so a Feature carrying a title alongside its own members is correct GeoJSON, and the strict schema turns it away. If you constrain a model this way, it cannot write the extra members your own pipeline may expect.
The other two are a gain, and an unplanned one. Closing the objects catches a crs member, the 2008 way of naming a coordinate reference system. RFC 7946 removed it without forbidding it, so it survives in the wild, and a file carrying one usually holds projected coordinates that a reader takes for degrees. Strictness catches it by accident: it refuses an unrecognised member without knowing what that member does.
Using it
Fetch the schema and hand it to whatever accepts one. It has no dependencies and refers only to itself.
bashcurl -O https://geojason.com/data/geojson.schema.jsonTwo properties of the file follow from the specification and not from preference, and both matter if you plan to edit it.
No object sets additionalProperties to false, because foreign members are permitted. Setting it is what the strict variant is for, and the table above is what that decision costs.
GeometryCollection refers back to itself, because RFC 7946 discourages nesting without forbidding it. Some constrained decoders cannot express a recursive schema and will reject the file outright. If yours does, replace the inner reference with a fixed depth and keep the type, because a GeometryCollection that cannot contain anything is not a GeometryCollection.
Whatever you constrain with, validate afterwards. The five rules above are the reason, and the validator here checks four of them.
Related
- Agent workflows, on what geometry costs in tokens and what to do when it does not fit.