prop/assets/js/beacon_map_hook.js
Graham McIntire ba0f1161a7 Beacon improvements: anon submit, notes, /map nav, admin backfill
- Anonymous users can submit beacons (held pending admin approval)
- Added notes field to beacons (textarea on form, shown on detail page)
- Added Beacons link to /map sidebar nav (mobile + desktop),
  removed stale Rover Planner link
- Moved /backfill to /admin/backfill under the admin live_session
- Beacon detail map zooms in two extra levels after fitBounds
2026-04-08 16:23:59 -05:00

68 lines
2.1 KiB
JavaScript

export const BeaconMap = {
mounted() {
const lat = parseFloat(this.el.dataset.lat)
const lon = parseFloat(this.el.dataset.lon)
const label = this.el.dataset.label || ""
const onTheAir = this.el.dataset.onTheAir === "true"
const cells = JSON.parse(this.el.dataset.cells || "[]")
const step = parseFloat(this.el.dataset.gridStep || "0.125")
const map = L.map(this.el, {
zoomControl: true,
attributionControl: false,
scrollWheelZoom: false
}).setView([lat, lon], 9)
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
maxZoom: 19
}).addTo(map)
// Draw each HRRR grid cell as a filled rectangle colored by its received
// signal tier. Use a single layerGroup so zoom/pan stays fast.
const cellLayer = L.layerGroup().addTo(map)
const half = step / 2.0
const allBounds = []
for (const c of cells) {
const sw = [c.lat - half, c.lon - half]
const ne = [c.lat + half, c.lon + half]
const rect = L.rectangle([sw, ne], {
color: c.color,
weight: 0,
fillColor: c.color,
fillOpacity: 0.55,
interactive: true
})
rect.bindTooltip(
`${c.label}<br>${c.rx_dbm} dBm<br>${c.distance_km} km · score ${c.score}`,
{sticky: true}
)
rect.addTo(cellLayer)
allBounds.push(sw, ne)
}
// Beacon marker on top.
const markerColor = onTheAir ? "#16a34a" : "#94a3b8"
const markerFill = onTheAir ? "#22c55e" : "#cbd5e1"
L.circleMarker([lat, lon], {
radius: 7,
color: markerColor,
fillColor: markerFill,
fillOpacity: 1.0,
weight: 2
}).addTo(map).bindTooltip(label, {
permanent: true,
direction: "top",
offset: [0, -10]
})
// Fit to the rendered cells (initial view already set above), then zoom
// in two steps so the beacon's immediate surroundings are visible.
if (allBounds.length > 0) {
map.fitBounds(L.latLngBounds(allBounds), {padding: [20, 20]})
map.setZoom(map.getZoom() + 2)
}
setTimeout(() => map.invalidateSize(), 50)
}
}