feat: support custom HTML/SVG element markers in MapLibre #10
@@ -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: `<svg width="32" height="32" viewBox="0 0 24 24" fill="#1a5276" xmlns="http://www.w3.org/2000/svg">` +
|
||||
`<path d="M3 17h18l-3-8H6L3 17zm9-14l-2 4h4l-2-4zM1 19h22v2H1v-2z"/>` +
|
||||
`</svg>`,
|
||||
Anchor: "center",
|
||||
Rotation: 45,
|
||||
Popup: &maplibre.Popup{
|
||||
Content: "<strong>Ferry</strong><p>Heading NE</p>",
|
||||
},
|
||||
})
|
||||
|
||||
// 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: `<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">` +
|
||||
`<circle cx="10" cy="10" r="9" fill="#9b59b6" stroke="#fff" stroke-width="2"/>` +
|
||||
`</svg>`,
|
||||
Anchor: "center",
|
||||
})
|
||||
|
||||
c.OnInterval(time.Second, func() {
|
||||
|
||||
@@ -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,`
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user