Skip to content

feat(maplibre): MapLibre GL plugin — interactive maps driven from Go#91

Merged
joaomdsg merged 2 commits into
mainfrom
feat/maplibre-plugin
Jun 3, 2026
Merged

feat(maplibre): MapLibre GL plugin — interactive maps driven from Go#91
joaomdsg merged 2 commits into
mainfrom
feat/maplibre-plugin

Conversation

@joaomdsg

@joaomdsg joaomdsg commented Jun 3, 2026

Copy link
Copy Markdown
Member

Adds plugins/maplibre: a MapLibre GL JS v5 plugin for building interactive map applications driven entirely from Go over SSE — no client JavaScript.

What's included

  • Construction (declarative): NewMap with center/zoom/pitch/bearing, controls, GeoJSON sources + layers, WithMarker static markers, GenerateFeatureIDs. Coordinates are typed — LngLat{Lng,Lat}/At(lng,lat) and Bounds{W,S,E,N} defuse the lng/lat & bbox swap foot-guns.
  • Outbound (Go → map): camera (FlyTo/EaseTo/JumpTo/SetCenter/FitBounds), markers (AddMarker/MoveMarker/RemoveMarker/ClearMarkers), GeoJSON data layers, Call/WithMapOption escape hatches.
  • Inbound (gestures → Go): OnClick, OnMoveEnd, OnMarkerClick, OnMarkerDragEnd, OnFeatureClick, and a generic OnMapEvent escape hatch — bound methods (via the via.Action constraint, no public any) read a typed MapEvent carrying the live camera.
  • Styling: typed expression builders (Get/FeatureState/Zoom/Boolean/Case/Interpolate/Step) + WhenHovered/WhenState sugar, replacing raw []any trees. WithFeatureHover highlights client-side via feature-state.
  • Dialogs: ShowPopup/ShowPopupHTML/ClosePopup — keyed server-driven popups; the HTML variant and marker PopupHTML take an h.H, so bodies compose with h.* (h.T escapes user data).

Notes / safety

  • Inbound-event signals use a non-underscore namespace — datastar silently drops _-prefixed local signals (caught via browser testing; guarded by a unit test).
  • XSS: user content goes through setText/h.T; all inlined JS values are unicode-escaped via mustJSON so </script> can't break out.
  • Multi-map pages are isolated by per-map registry slots (verified).

Verification

  • Full unit + integration test suite green (go test ./...), go vet, gofmt clean.
  • Browser-verified on localhost: click-to-place, marker click/drag, feature click, hover-highlight, right-click, multi-map routing, and popups.

Docs in plugins/maplibre/doc.go; runnable demo in internal/examples/maps.

Add plugins/maplibre, a MapLibre GL JS v5 plugin mirroring the echarts
design: the plugin injects the CDN JS + (required) CSS and a per-page map
registry, and a *Map drives the camera, markers, and GeoJSON data layers
from Go over SSE.

- Construction: center/zoom/pitch/bearing, zoom range, max bounds,
  navigation/scale/geolocate/fullscreen controls, GeoJSON sources + layers,
  and a WithMapOption escape hatch.
- Camera: FlyTo/EaseTo/JumpTo/SetCenter/SetZoom/SetPitch/SetBearing/FitBounds.
- Markers: keyed AddMarker/MoveMarker/RemoveMarker/ClearMarkers with
  Color/Draggable/Scale and XSS-safe PopupText (plus trusted-only PopupHTML).
- Data: GeoJSON + layer builders; SetGeoJSON/AddGeoJSONSource/AddLayer/
  SetPaintProperty/SetLayerVisibility/RemoveLayer(AndSource), guarded by a
  re-arming style-load wait.
- Lifecycle: SetStyle/Resize/Dispose and a Call escape hatch.

Add internal/examples/maps (server-driven world map) and document the
plugin in docs/plugins.md.
Comment thread plugins/maplibre/events.go Fixed
@joaomdsg
joaomdsg force-pushed the feat/maplibre-plugin branch 2 times, most recently from 5545c7a to 293e99b Compare June 3, 2026 19:13
…coords

Build the plugin into a full interactive-map toolkit, all driven from Go.

Inbound events: OnClick, OnMoveEnd, OnMarkerClick, OnMarkerDragEnd,
OnFeatureClick, and a generic OnMapEvent escape hatch route browser gestures
to bound methods, read via Map.Event(ctx). Handlers use the via.Action
func-union constraint (no public `any`). Every gesture carries the live camera
(zoom/bearing/pitch). WithFeatureHover highlights features client-side via
feature-state; clickable markers/features show a pointer cursor.

Styling: typed expression builders (Get, FeatureState, Zoom, Boolean, Case,
Interpolate, Step) plus WhenHovered/WhenState sugar replace hand-written
[]any expression trees.

Coordinates: LngLat{Lng,Lat} + At() and Bounds{West,South,East,North} replace
bare float pairs to defuse the lng/lat and bbox swap foot-guns; MapEvent.LngLat()
flows a gesture straight into the camera/marker APIs. WithMarker declares static
markers at construction (no OnConnect); GenerateFeatureIDs assigns feature ids
for feature-state.

Dialogs: ShowPopup / ShowPopupHTML / ClosePopup show keyed, server-driven
popups. The HTML variant and marker PopupHTML take an h.H, so popup bodies
compose with the same h.* builders (h.T escapes user data; only h.Raw is raw).

Inbound-event signals use a non-underscore namespace (datastar drops
_-prefixed local signals). Multi-map pages are isolated by per-map registry
slots. Documented in doc.go and demonstrated in internal/examples/maps.
@joaomdsg
joaomdsg force-pushed the feat/maplibre-plugin branch from 293e99b to 64220ac Compare June 3, 2026 19:20
@joaomdsg
joaomdsg merged commit 7fe6ba8 into main Jun 3, 2026
6 checks passed
@joaomdsg
joaomdsg deleted the feat/maplibre-plugin branch June 3, 2026 19:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants