The short version
A geological map viewer is not a basemap with colored polygons on top. The features that actually matter are the ones that let a geologist interrogate the map: click a unit and read its description, age, and source; filter to a single formation; compare geology against terrain or imagery with a swipe; and see where the linework is approximate rather than certain. A viewer that renders beautifully but answers none of these questions is a screenshot with zoom controls. Below are the features worth building, why each one matters to a technical user, and how they map onto MapLibre GL, deck.gl, and 3D tiling stacks.
Queryable features, not painted pixels
The first feature that separates a real viewer from an image tile is identify. Serve the geology as vector tiles (Mapbox Vector Tiles via a pmtiles archive or a tile server such as Martin or pg_tileserv from PostGIS) so the unit and fault attributes travel with the geometry to the browser. In MapLibre GL JS, wire a click handler:
map.on('click', 'geology-units', (e) => {
const f = map.queryRenderedFeatures(e.point, { layers: ['geology-units'] })[0];
new maplibregl.Popup()
.setLngLat(e.lngLat)
.setHTML(`<strong>${f.properties.MapUnit}</strong><br>${f.properties.age}<br>${f.properties.description}`)
.addTo(map);
});
The popup should expose the same fields your QA process enforced — map-unit code, age range, lithologic description, data source, and confidence — so the viewer becomes an interface to the data model rather than a flattening of it. For very large polygon counts or analytical overlays (heatmaps of sample density, interpolated surfaces), a deck.gl GeoJsonLayer or MVTLayer rendered on the same map view gives you GPU performance that the base MapLibre layers will not.
Data-driven, scale-aware legend and layer control
The legend must be generated from the style, not pasted in as an image, so it cannot drift from what is drawn. Order units by stratigraphic age (youngest at top), match the swatch to the exact fill color and pattern used in the style's fill-color expression, and group faults and contacts with their line symbology (solid for certain, dashed for approximate, dotted for concealed).
Pair this with scale-dependent visibility. Geology compiled at 1:24,000 should not be drawn over a whole-continent zoom; set minzoom/maxzoom on the source layers so detail appears only where the source scale supports it. This is both a legibility decision and an honesty decision — showing fine contacts at zoom level 4 implies precision that does not exist.
Comparison tools
Geologists rarely look at geology in isolation; they compare it to a hillshade, to satellite imagery, or to an older edition. The high-value comparison features are:
- Swipe / compare slider between geology and an imagery or hillshade layer.
- Per-layer opacity so a unit can be made translucent over terrain.
- Attribute filter, e.g. a control that calls
map.setFilter('geology-units', ['==', 'MapUnit', 'Qa'])to isolate one formation, or a time/age slider that filters by age range.
These turn the viewer from a display into an analysis tool, which is the difference that makes technical users return to it.
Honest 2D versus 3D
3D is the most over-applied feature in geological viewers. Use 2D as the default because it preserves accurate area comparison and measurement. Reach for 3D only when depth is the point: draping geology over a DEM with terrain exaggeration to read structural relief, or MapLibre terrain with a raster-dem source plus a draped geology layer. For genuinely volumetric data — drillholes, modeled surfaces, point clouds — a Cesium + 3D Tiles stack is appropriate, but it costs you the smooth filtering and querying that 2D gives cheaply. The test is simple: if a flat map answers the user's question, do not add a globe.
Performance with real data volume
Test with the full dataset, not a clipped sample. A national geology layer is tens of megabytes of GeoJSON, which will stall a browser; convert it to vector tiles (tippecanoe -o geology.pmtiles --drop-densest-as-needed geology.geojson) and serve the .pmtiles directly. Simplify geometry per zoom level so a zoomed-out view does not ship full-resolution coastlines of every polygon. Watch the number of distinct rendered features; deck.gl handles hundreds of thousands of features on the GPU, while a naive GeoJSON source in MapLibre does not.
A short build sequence
- Decide the user's core questions (identify a unit, compare to terrain, isolate a formation).
- Convert source geology to vector tiles (
tippecanoeto PMTiles) preserving the attribute fields. - Build a MapLibre style with
fill-colorkeyed on MapUnit andminzoom/maxzoomper layer. - Add the click-to-identify popup reading real attributes.
- Add a swipe control against a hillshade or imagery source.
- Add a filter/legend that drives
setFilter. - Profile with the full dataset; only then consider 3D.
Common pitfalls and why they happen
- Shipping raw desktop exports to the browser, because "it worked in QGIS." Desktop renders from disk; the browser must download and parse everything. The fix is a tiling step.
- Static legend images that no longer match the data after a unit rename — the cause is decoupling the legend from the style.
- 3D for impressiveness rather than need, which trades away the filtering and measurement users actually use.
- Hiding source and confidence behind a polished UI, which makes the map untrustworthy precisely to the audience that cares — surface them in the popup and a persistent source note.
Validation summary
Before launch, confirm: clicking any unit returns its real attributes; the legend matches the rendered colors and stratigraphic order; layers appear and disappear at sensible zooms; the comparison/filter controls actually change what is drawn; the full dataset loads and pans without stalling; and source and confidence are visible. If 3D is present, confirm it answers a question 2D could not.
Bathyl perspective
We build viewers so a specialist can interrogate geology and a decision-maker can read it, without either losing the science in between. Every feature above earns its place by answering a real question; anything that only looks good gets cut.
Related reading
- From Static Geological Map to Web Map
- Geological Map Legends for Interactive Interfaces
- Preparing GIS Data for Web Visualization
- Mapping systems