Content Type: Geospatial
Changes to how this Type behaves are requested through a GitHub Issue and implemented, together with this reference page, through the repository's normal workflow. See /_docs/posts for how Content Types, Roles, and Rank work in general, and docs/Internal/type-doc-templates.md for the Content Type Doc template this file follows.
A location, path, area, or point-density map — a Point, a LineString, a Polygon, or a Heatmap.
Fields
- Geometry type (mandatory) —
point,linestring,polygon, orheatmap. - Data (mandatory) — a list of Lat/Long pairs, interpreted per Geometry Type:
- Point — exactly 1 pair.
- LineString — 2 or more pairs, ordered, an open path from first to last.
- Polygon — 3 or more pairs, ordered, a closed loop (the last pair implicitly connects back to the first — there's no need to repeat it).
- Heatmap — 2 or more pairs, unordered, unweighted — every point counts equally toward the density shown. Renders as shaded density rather than a path or shape.
- Label (key optional) — a caption.
- Descriptive metadata (optional) — a small set of fields (a label, precise location, rights, and similar) distinct from the Label above — mainly useful for an entry brought in from elsewhere, to preserve facts about where it came from. Not currently rendered on the page itself. See /_docs/json-import for the full field list.
This is a superset of the older Point-only shape: a Point has always effectively been a 1-item list, so nothing about existing content needed to change when Heatmap and the other three Geometry Types' guided input shipped.
Semantic HTML and accessibility
Renders as a Leaflet map mounted into a plain <div class="content-map"> (see client/src/lib/map.ts) — an interactive canvas, not a table of coordinates or an image, so a screen reader gets no structured description of the geometry itself. A visible textual companion always accompanies it instead: the Label (if present) alongside the map itself, a coordinates/point-count line and any public descriptive-metadata fields on the standalone Content Element page (below), and a label-per-element summary list beneath the Additional Information combined map.
Interaction: every embedded (non-fullscreen) map carries a small, always-visible Fullscreen button, never hover-revealed (a map is already interactive via pan/zoom, so an always-visible affordance is the clearer signal). A single Geospatial entry's own map (an inline {{map:role}} embed, the standalone Content Element page's own map) additionally carries an in-map ⓘ button opening that element's own /_element/geospatial/:id details page — omitted on the standalone page itself, since that page already is the details view. The Additional Information combined map and a Category/Tag collection's own combined map identify a clicked feature directly (opening that element's or Post's own page) rather than through a separate ⓘ control, since every feature there is already individually clickable to that end. Ctrl+scroll (⌘+scroll on macOS) zooms an embedded map; scrolling over it without the modifier scrolls the page instead and briefly shows an on-map hint with the correct key for the visitor's platform, also announced to screen readers via a live region. Touch panning/zooming an embedded map requires two fingers, so a single finger still scrolls the page. Opening fullscreen preserves a single entry's current pan/zoom (no modifier needed there — normal direct pan/zoom applies once the page-scroll conflict no longer exists); a "Show all" control re-fits to the full geometry on demand. Escape or the close button exits back to exactly where you were; the Left/Right Arrow keys are never intercepted as image-viewer navigation while a map has the fullscreen viewer open, since the two fullscreen viewers (media, map) are mutually exclusive, never both mounted at once.
Editing: the full-screen map editor
Every Geometry Type is entered the same way — a full-screen map editor, opened from the block's "Edit on map" button:
- Click empty map area to add a point. For Point specifically, a second click relocates the one point rather than adding another — a Point can never hold more than one.
- Click an existing point to remove it.
- Drag an existing point to reposition it.
- LineString and Polygon draw a live connecting line/shape as points are added; Heatmap shows a live density preview instead.
Nothing is saved to the Post until the editor's own Save button is clicked, and even then nothing is published until the Post itself is saved (Publish or Save Draft) — same as any other field.
Validation: saving with fewer points than a Geometry Type's minimum is blocked, with a clear message, never silently padded. Switching Geometry Type in the block's dropdown is blocked the same way if the current point list doesn't already fit the new type's rules (e.g. going from a 5-point LineString to Point) — adjust the point count on the map first, then switch.
Ingestion: import a file, or paste coordinates
Above the list of Geospatial blocks, an "Import…" control offers two ways to skip drawing by hand:
- File upload or drag-drop — GPX, KML, KMZ (a zipped KML), or CSV. Geometry Type is inferred from the file's own structure: a GPX waypoint or KML Point placemark becomes a Point, a GPX track/route or KML LineString becomes a LineString, a KML Polygon becomes a Polygon. A CSV is just a list of Lat/Long pairs (a header row like
lat,lngis auto-detected if present, otherwise the first two numeric columns are used) and becomes a Point (one row) or a LineString (more than one), same rule freeform paste below uses.- A file containing more than one feature (a GPX with several tracks, a KML with several placemarks) creates one Geospatial block per feature directly, with no individual review step — the same batch pattern already used for multi-file Image upload.
- A file with exactly one feature opens straight into the map editor for review and adjustment before saving.
- Paste coordinates — a textarea for raw text containing "lat, lng" pairs, e.g. copied straight out of Google Earth's "copy coordinates" action. A single pair becomes a Point; more become a LineString. Always opens the map editor afterward for review, same as a single-feature file import.
Fetch by URL is deliberately not offered for Geospatial — only Upload, drag-drop, and paste, unlike Image/Video/Audio's three-way ingestion.
Role, Rank, and Reference
A Post can hold several Geospatial entries at once, each with its own Role, or several sharing the same Role, distinguished by Rank — see /_docs/posts. Every Geospatial entry also has an optional Reference — a separate, Post-unique name for addressing it inline instead of by Role/Rank — see /_docs/posts's "Reference: inline addressing" for the full detail.
Rendering contexts
Inline
{{map:route-start}} renders an embedded map for the Geospatial element with Role route-start, using its point/line/polygon/heatmap data — backed by OpenStreetMap, no account or key involved. See /_docs/markdown for the full content reference syntax.
Additional Information group
Every Post Type's shared Additional Information container's Geospatial group — every eligible Geospatial element on the resolved Post Version together on one combined map, in Rank-then-stable order, rather than one map per element. The initial viewport fits all included geometry. Several elements sharing the exact same Point location are grouped into a single marker showing a selection popup (same pattern used by the Category/Tag collection map) rather than stacking identical markers; Line/Polygon/Heatmap elements are never grouped this way, since their outlines stay visually distinguishable even overlapping. Selecting a feature opens that specific element's own /_element/geospatial/:id page. A compact, always-visible textual summary list (every included element's Label, each linking to its own metadata page) sits beneath the map — the accessible/non-map companion, not a fallback gated behind a missing-map state.
This combined map has its own Fullscreen control (client/src/components/AdditionalInformation.tsx's GeospatialGroup/GeospatialGroupFullscreen, a direct copy of the Category/Tag collection map's own CollectionMap/CollectionMapFullscreen shell — same portal, focus, Escape, and history-back handling) but deliberately no ⓘ control, matching that same precedent: a multi-feature map has no single "details" page to point one at, and every feature is already individually clickable (mouse/touch) or reachable via the always-visible list (keyboard).
Post metadata-page traversal
Every Geospatial entry the Post has ever held (current Version, or full history via /metadata/history) is enumerated with its Geometry Type, point data, and Label as JSON-LD/native JSON — see /_docs/posts's Metadata page section.
Standalone Content Element page
/_element/geospatial/:id shows the Label (or a generic "<type> entry") as a compact heading, gated exactly like the owning Post's own page. It also shows a real embedded map of that one element (no ⓘ control on it, since this page already is the details view — Fullscreen still available), a coordinates/point-count summary line beneath the map, and the public descriptive-metadata fields (Description, Creator, Source, Original date, Rights) when present. Detailed raw geometry, validation state, and any import/processing diagnostics stay Admin-only, same public/Admin split every other Content Type's own metadata already follows.