Exporting Lineage Graphs to GeoJSON for Web Maps
Part of: Lineage Visualization and Reporting
A node-link diagram is the wrong picture for a question that is fundamentally about place. “Which parts of this coverage came from the 2019 survey and which from the 2024 one?” is answered by a map, not by a DAG, and the lineage system already holds the extents needed to draw it. This how-to exports lineage as GeoJSON so it can be rendered on a map, and covers the modelling decision that makes or breaks it — what, exactly, is the geometry of a derivation?
The answer to that question is the whole guide: the geometry is the region of the output that a given source contributed to. Not the source’s extent, not the output’s extent, but the intersection where the contribution actually happened. Getting this wrong produces a map that looks convincing and is wrong at every boundary.
Prerequisites
- Dataset extents in the spatial store, per Lineage Visualization and Reporting.
- Contribution regions per derivation edge, or a way to compute them.
- Geometries reprojectable to WGS 84, which GeoJSON requires.
- A size budget, because GeoJSON is verbose.
The Geometry of a Derivation Is the Contribution Region
This is the modelling decision everything else depends on, and the intuitive choice is the wrong one.
The partition property in the caption is the strongest assertion available here and it is cheap to check. Union the contribution regions and compare against the output extent: a gap means a region with no recorded source, an overlap means two sources credited for the same ground, and both are real defects rather than rendering artefacts.
Where the pipeline genuinely blends sources — a feathered mosaic, a weighted interpolation — the regions overlap by design. Record the blend weight as a property and state in the export that regions may overlap, rather than forcing a partition that misrepresents what happened.
Where contribution regions were never recorded, do not fabricate them by intersecting extents. That produces plausible geometry with no basis, and it will be used. Export the extent with an explicit property saying the contribution region is unknown.
Model Nodes as Features, Edges as Properties
GeoJSON has no edge concept, and the natural workarounds are all worse than the simple one.
Emit one feature per contribution region, carrying the source identifier, the output identifier, the activity, and the date as properties. Each feature is then a derivation, which is what a reader wants to click on, and the graph structure is recoverable from the identifier properties without needing to be drawn.
Resist emitting edges as LineString features connecting extent centroids. It renders as a spider’s web over the map, it obscures the coverage it sits on, and the centroid of a coverage extent is not a place anything happened.
Where the graph structure genuinely matters more than the geography, use a DAG rendering instead and link to the map. The two views answer different questions and trying to make one do both produces something that answers neither.
Keep a separate small feature collection for the output extents themselves, so a map can draw the boundary of what is being explained. It is one feature per output and it makes the contribution regions legible.
Size Is the Binding Constraint
GeoJSON of real geometry is large, and a lineage export that includes full-precision boundaries will be tens of megabytes before anyone notices.
Six decimal places is the right default and cutting further is rarely worth the risk. Eleven centimetres is finer than any lineage boundary is meaningful to, and the saving from five places is small compared to what simplification gives.
Simplification is the one reduction that changes the data, so it must be declared. A contribution boundary simplified to a fifty-metre tolerance and then used to answer “which survey covers this parcel?” gives wrong answers near every edge, and only the recorded tolerance warns the reader.
Prune properties aggressively and fetch detail on click. Six properties per feature is enough to render, label and identify; everything else is one request away and most features are never clicked.
Reprojection Is Mandatory and Lossy
GeoJSON is defined in WGS 84, and lineage geometry is usually stored in a projected system, so every export reprojects.
Record the source CRS and the transformation used as properties on the collection. A reprojected boundary carries an accuracy penalty, and a reader comparing the export against the projected original needs to know why they differ.
Reproject before simplifying, not after. Simplification tolerances are expressed in the units of the geometry, and a tolerance chosen in metres applied after reprojection to degrees is wrong by a factor that varies with latitude.
Never round-trip through the export. GeoJSON is a delivery format; treating it as a storage format means the reprojection and simplification losses compound each time, and after three cycles the boundaries have moved measurably.
Make the Map Answer a Question
An export is only worth producing if the rendered result answers something a table could not, and stating the question first prevents a generic overlay nobody uses.
The gap row is the one most often missed. A map of contribution regions shows what exists; the region with no contribution recorded is simply blank, and blank reads as background rather than as a finding. Compute the difference between the output extent and the union of contributions, and export it as its own feature.
Export only the properties the chosen styling needs, plus an identifier. A generic export carrying everything invites each consumer to invent their own styling, and the map then means something different in every application.
State the vintage of the export itself in the collection metadata. A lineage map is a snapshot, and one left on a wall for a year will be read as current.
Verification
Assert the partition property: union the contribution regions of one output and compare against its extent, allowing a small tolerance. Gaps and overlaps must be reported rather than silently accepted.
Assert the declared tolerance matches the applied one by measuring the maximum deviation between simplified and original geometry, and confirming it is within what the property claims.
Assert coordinate order. GeoJSON is longitude-first, and a library configured for latitude-first produces an export that renders in the wrong hemisphere — obvious once seen and easy to ship.
Assert size against the budget in the test suite, with a fixture representative of a large real export. Size regressions arrive through added properties and are invisible until a browser stalls.
Gotchas & edge cases
- Antimeridian crossing splits polygons. A coverage spanning 180 degrees must be emitted as a multipart geometry or it renders as a band across the whole world. Test with a Pacific fixture if you have any.
- Newline-delimited GeoJSON streams; plain GeoJSON does not. A single feature collection must be parsed whole, so a large export stalls the browser before anything renders.
- GeoJSON has no CRS member any more. The current specification removed it, so the source CRS must travel as a property or in accompanying metadata. Relying on the old member means it will be ignored.
- Overlapping features render in file order. Where contributions legitimately overlap, the last one drawn wins visually and the reader sees one source where there are two. Sort deliberately and add a legend note.
- Empty and null geometries are different. A derivation with no spatial extent should be omitted or given a null geometry deliberately; an empty polygon renders as nothing and looks like a bug.
- Property name collisions with renderer conventions. A property called
id,typeorstylemay be consumed by the mapping library rather than displayed. Prefix lineage properties. - Precision is not accuracy. Six decimal places on a boundary derived from thirty-metre imagery advertises eleven-centimetre certainty that does not exist. Carry the accuracy as a property so the coordinate precision is not read as a claim.
- A large collection is not a tile set. Past a few megabytes the right answer is vector tiles rather than a bigger GeoJSON, and the conversion is easier before consumers have been built against the flat file.
- Winding order matters to some renderers. The specification requires right-hand rule for exterior rings; not every producer complies and not every consumer forgives it.
Related
- Lineage Visualization and Reporting — choosing the right view
- Rendering Lineage DAGs with Graphviz in Python — the structural view
- CRS and Datum Transformation Provenance — recording the reprojection this performs
- Redacting Sensitive Locations from Lineage Exports — what must not leave in this file
- Part of: Lineage Visualization and Reporting