diff --git a/assets/js/beacon_map_hook.ts b/assets/js/beacon_map_hook.ts
index 348ff5e8..14d01ec2 100644
--- a/assets/js/beacon_map_hook.ts
+++ b/assets/js/beacon_map_hook.ts
@@ -12,6 +12,12 @@ interface ToggleCoveragePayload {
show: boolean
}
+interface LoadCoveragePayload {
+ grid_step: number
+ cells: CoverageCell[]
+ show: boolean
+}
+
interface BeaconMapHook extends ViewHook {
mounted(this: BeaconMapHook): void
}
@@ -23,9 +29,6 @@ export const BeaconMap = {
const lon = parseFloat(dataset.lon!)
const label = dataset.label || ""
const onTheAir = dataset.onTheAir === "true"
- const cells: CoverageCell[] = JSON.parse(dataset.cells || "[]")
- const step = parseFloat(dataset.gridStep || "0.125")
- const initialShowCoverage = dataset.showCoverage === "true"
const map = L.map(this.el, {
zoomControl: true,
@@ -37,30 +40,6 @@ export const BeaconMap = {
maxZoom: 19
}).addTo(map)
- // Build the coverage cell layer up-front but keep it off the map until
- // the user toggles it on. Using a single layerGroup keeps pan/zoom fast.
- const cellLayer = L.layerGroup()
- const half = step / 2.0
- const allBounds: [number, number][] = []
-
- for (const c of cells) {
- const sw: [number, number] = [c.lat - half, c.lon - half]
- const ne: [number, number] = [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}
${c.rx_dbm} dBm
${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"
@@ -76,20 +55,59 @@ export const BeaconMap = {
offset: [0, -10]
})
+ // Coverage layer is lazy — populated on the first `load_coverage`
+ // event, which the server only sends when the user flips the
+ // toggle on. Keeping this off the mount path avoids parsing tens
+ // of thousands of cells into Leaflet rects on every page load.
+ let cellLayer: L.LayerGroup | null = null
+ let cellBounds: [number, number][] = []
+
+ const buildLayer = (cells: CoverageCell[], step: number): void => {
+ if (cellLayer) {
+ cellLayer.clearLayers()
+ } else {
+ cellLayer = L.layerGroup()
+ }
+ cellBounds = []
+ const half = step / 2.0
+
+ for (const c of cells) {
+ const sw: [number, number] = [c.lat - half, c.lon - half]
+ const ne: [number, number] = [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}
${c.rx_dbm} dBm
${c.distance_km} km · score ${c.score}`,
+ {sticky: true}
+ )
+ rect.addTo(cellLayer)
+ cellBounds.push(sw, ne)
+ }
+ }
+
const showLayer = (): void => {
+ if (!cellLayer) return
if (!map.hasLayer(cellLayer)) cellLayer.addTo(map)
- if (allBounds.length > 0) {
- map.fitBounds(L.latLngBounds(allBounds), {padding: [20, 20]})
+ if (cellBounds.length > 0) {
+ map.fitBounds(L.latLngBounds(cellBounds), {padding: [20, 20]})
map.setZoom(map.getZoom() + 2)
}
}
const hideLayer = (): void => {
- if (map.hasLayer(cellLayer)) map.removeLayer(cellLayer)
+ if (cellLayer && map.hasLayer(cellLayer)) map.removeLayer(cellLayer)
map.setView([lat, lon], 11)
}
- if (initialShowCoverage) showLayer()
+ this.handleEvent("load_coverage", ({grid_step, cells, show}: LoadCoveragePayload) => {
+ buildLayer(cells, grid_step)
+ if (show) showLayer()
+ })
this.handleEvent("toggle_coverage", ({show}: ToggleCoveragePayload) => {
if (show) {
diff --git a/lib/microwaveprop_web/live/beacon_live/show.ex b/lib/microwaveprop_web/live/beacon_live/show.ex
index 9ea631f9..3b97918a 100644
--- a/lib/microwaveprop_web/live/beacon_live/show.ex
+++ b/lib/microwaveprop_web/live/beacon_live/show.ex
@@ -53,9 +53,6 @@ defmodule MicrowavepropWeb.BeaconLive.Show do
data-lon={@beacon.lon}
data-label={"#{@beacon.callsign} · #{Beacon.format_freq(@beacon.frequency_mhz)} MHz"}
data-on-the-air={to_string(@beacon.on_the_air)}
- data-grid-step={@estimate.grid_step}
- data-cells={Jason.encode!(@estimate.cells)}
- data-show-coverage={to_string(@show_coverage)}
>
@@ -78,7 +75,10 @@ defmodule MicrowavepropWeb.BeaconLive.Show do
-