From 934805e707d9568c3909d6dc00a789da0231d2b4 Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 20 Feb 2026 20:40:19 +0000 Subject: [PATCH] feat: support custom HTML/SVG element markers in MapLibre (#10) --- internal/examples/maplibre/main.go | 20 ++++++++++++++++++-- maplibre/js.go | 17 ++++++++++++++++- maplibre/types.go | 13 +++++++++++++ 3 files changed, 47 insertions(+), 3 deletions(-) diff --git a/internal/examples/maplibre/main.go b/internal/examples/maplibre/main.go index 21b606b..f6610e7 100644 --- a/internal/examples/maplibre/main.go +++ b/internal/examples/maplibre/main.go @@ -75,14 +75,30 @@ func main() { }, }) - // Purple vehicle marker — reads shared Go state + // Custom SVG ship marker (static) + m.AddMarker("ship", maplibre.Marker{ + LngLat: maplibre.LngLat{Lng: -122.38, Lat: 37.80}, + Element: `` + + `` + + ``, + Anchor: "center", + Rotation: 45, + Popup: &maplibre.Popup{ + Content: "Ferry

Heading NE

", + }, + }) + + // Custom SVG vehicle marker — reads shared Go state vehicleLng := c.Signal(-122.43) vehicleLat := c.Signal(37.77) m.AddMarker("vehicle", maplibre.Marker{ LngSignal: vehicleLng, LatSignal: vehicleLat, - Color: "#9b59b6", + Element: `` + + `` + + ``, + Anchor: "center", }) c.OnInterval(time.Second, func() { diff --git a/maplibre/js.go b/maplibre/js.go index 67952f1..7137daa 100644 --- a/maplibre/js.go +++ b/maplibre/js.go @@ -179,10 +179,25 @@ func initScript(m *Map) string { // markerBodyJS generates JS to add a marker, assuming `map` is in scope. func markerBodyJS(mapID, markerID string, mk Marker) string { var b strings.Builder + + if mk.Element != "" { + b.WriteString(fmt.Sprintf( + `var _mkEl=document.createElement('div');_mkEl.innerHTML=%s;`, + jsonStr(mk.Element))) + } + opts := "{" - if mk.Color != "" { + if mk.Element != "" { + opts += `element:_mkEl.firstElementChild||_mkEl,` + } else if mk.Color != "" { opts += fmt.Sprintf(`color:%s,`, jsonStr(mk.Color)) } + if mk.Anchor != "" { + opts += fmt.Sprintf(`anchor:%s,`, jsonStr(mk.Anchor)) + } + if mk.Rotation != 0 { + opts += fmt.Sprintf(`rotation:%s,`, formatFloat(mk.Rotation)) + } if mk.Draggable { opts += `draggable:true,` } diff --git a/maplibre/types.go b/maplibre/types.go index c9fa611..5772772 100644 --- a/maplibre/types.go +++ b/maplibre/types.go @@ -302,6 +302,19 @@ type Marker struct { Draggable bool Popup *Popup + // Element is raw HTML/SVG used as a custom marker instead of the + // default pin. When set, Color is ignored. + // Do not pass untrusted user input without sanitizing it first. + Element string + + // Anchor controls which part of the element sits at the coordinate. + // Values: "center" (default for custom elements), "bottom" (default + // for the pin), "top", "left", "right", "top-left", etc. + Anchor string + + // Rotation is clockwise degrees. Useful for directional icons (ships, vehicles). + Rotation float64 + // Signal-backed position. When set, signals drive marker position reactively. // Initial position is read from the signal values. LngLat is ignored when signals are set. // If Draggable is true, drag updates write back to these signals.