From cbc5022e0ddd27009778651f110d2538a52ced3f Mon Sep 17 00:00:00 2001 From: ryan Date: Fri, 20 Feb 2026 20:16:17 +0000 Subject: [PATCH] feat: sync all markers across clients in MapLibre example (#9) --- internal/examples/maplibre/main.go | 85 +++++++++++++++++++++++++----- 1 file changed, 72 insertions(+), 13 deletions(-) diff --git a/internal/examples/maplibre/main.go b/internal/examples/maplibre/main.go index 9fb4af8..21b606b 100644 --- a/internal/examples/maplibre/main.go +++ b/internal/examples/maplibre/main.go @@ -2,6 +2,8 @@ package main import ( "math/rand" + "strconv" + "sync" "time" "github.com/ryanhamamura/via" @@ -9,6 +11,19 @@ import ( "github.com/ryanhamamura/via/maplibre" ) +type posMsg struct { + Lng float64 `json:"lng"` + Lat float64 `json:"lat"` +} + +var ( + vehicleOnce sync.Once + vehicle struct { + mu sync.RWMutex + lng, lat float64 + } +) + func main() { v := via.New() v.Config(via.Options{ @@ -18,6 +33,21 @@ func main() { Plugins: []via.Plugin{maplibre.Plugin}, }) + // Single goroutine moves the vehicle — all clients read the same position. + vehicle.lng = -122.43 + vehicle.lat = 37.77 + vehicleOnce.Do(func() { + go func() { + for { + time.Sleep(time.Second) + vehicle.mu.Lock() + vehicle.lng = -122.43 + (rand.Float64()-0.5)*0.02 + vehicle.lat = 37.77 + (rand.Float64()-0.5)*0.02 + vehicle.mu.Unlock() + } + }() + }) + v.Page("/", func(c *via.Context) { m := maplibre.New(c, maplibre.Options{ Style: "https://demotiles.maplibre.org/style.json", @@ -45,7 +75,7 @@ func main() { }, }) - // Signal-backed marker — server pushes position updates + // Purple vehicle marker — reads shared Go state vehicleLng := c.Signal(-122.43) vehicleLat := c.Signal(37.77) @@ -56,12 +86,37 @@ func main() { }) c.OnInterval(time.Second, func() { - vehicleLng.SetValue(-122.43 + (rand.Float64()-0.5)*0.02) - vehicleLat.SetValue(37.77 + (rand.Float64()-0.5)*0.02) + vehicle.mu.RLock() + lng, lat := vehicle.lng, vehicle.lat + vehicle.mu.RUnlock() + vehicleLng.SetValue(lng) + vehicleLat.SetValue(lat) c.SyncSignals() }) - // Draggable marker — user drags, signals update + // Yellow click marker — synced across clients via PubSub + clickLng := c.Signal(-122.4194) + clickLat := c.Signal(37.7749) + + m.AddMarker("clicked", maplibre.Marker{ + LngSignal: clickLng, + LatSignal: clickLat, + Color: "#f39c12", + }) + + via.Subscribe(c, "map.click", func(msg posMsg) { + clickLng.SetValue(msg.Lng) + clickLat.SetValue(msg.Lat) + c.SyncSignals() + }) + + click := m.OnClick() + handleClick := c.Action(func() { + e := click.Data() + via.Publish(c, "map.click", posMsg{Lng: e.LngLat.Lng, Lat: e.LngLat.Lat}) + }) + + // Blue draggable pin — synced across clients via PubSub pinLng := c.Signal(-122.41) pinLat := c.Signal(37.78) @@ -72,14 +127,16 @@ func main() { Draggable: true, }) - // Click event — click to place a marker - click := m.OnClick() - handleClick := c.Action(func() { - e := click.Data() - m.AddMarker("clicked", maplibre.Marker{ - LngLat: e.LngLat, - Color: "#f39c12", - }) + via.Subscribe(c, "map.pin", func(msg posMsg) { + pinLng.SetValue(msg.Lng) + pinLat.SetValue(msg.Lat) + c.SyncSignals() + }) + + handlePinDrag := c.Action(func() { + lng, _ := strconv.ParseFloat(pinLng.String(), 64) + lat, _ := strconv.ParseFloat(pinLat.String(), 64) + via.Publish(c, "map.pin", posMsg{Lng: lng, Lat: lat}) }) // GeoJSON polygon source + fill layer @@ -111,7 +168,7 @@ func main() { }, }) - // FlyTo actions using CameraOptions + // FlyTo actions zoom14 := 14.0 flyToSF := c.Action(func() { m.FlyTo(maplibre.CameraOptions{ @@ -134,6 +191,7 @@ func main() { h.H1(h.Text("MapLibre GL Example")), m.Element( click.Input(handleClick.OnInput()), + h.Input(h.Type("hidden"), pinLng.Bind(), handlePinDrag.OnInput()), ), h.Div(h.Attr("style", "margin-top:1rem;display:flex;gap:0.5rem;flex-wrap:wrap"), h.Button(h.Text("Fly to San Francisco"), flyToSF.OnClick()), @@ -142,6 +200,7 @@ func main() { h.Div(h.Attr("style", "margin-top:0.5rem;font-size:0.9rem"), h.P(h.Text("Zoom: "), m.Zoom.Text()), h.P(h.Text("Center: "), m.CenterLng.Text(), h.Text(", "), m.CenterLat.Text()), + h.P(h.Text("Click: "), clickLng.Text(), h.Text(", "), clickLat.Text()), h.P(h.Text("Vehicle: "), vehicleLng.Text(), h.Text(", "), vehicleLat.Text()), h.P(h.Text("Draggable Pin: "), pinLng.Text(), h.Text(", "), pinLat.Text()), ),