Optimize map performance: reduce payload 75%, faster rendering
- Remove factors from heatmap scores, fetch on-demand via point_detail - Pre-encode initial scores JSON once instead of per-render - Don't store scores in socket assigns (reduce memory) - Pre-calculate RGBA color lookup table for canvas rendering - Batch fillStyle changes, hoist math out of inner loop - Show topbar immediately on data requests (no 300ms delay)
This commit is contained in:
parent
2f2f3388cd
commit
1aebd7f1cc
2 changed files with 32 additions and 8 deletions
|
|
@ -264,14 +264,21 @@ export const PropagationMap = {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// Click on map to show factor breakdown + range circle
|
// Click on map to request factor detail from server
|
||||||
this.map.on("click", (e) => {
|
this.map.on("click", (e) => {
|
||||||
const detail = this.lookupPoint(e.latlng.lat, e.latlng.lng)
|
const basic = this.lookupPoint(e.latlng.lat, e.latlng.lng)
|
||||||
|
if (basic) {
|
||||||
|
this.showRangeCircles(basic)
|
||||||
|
this.pushEvent("point_detail", { lat: e.latlng.lat, lon: e.latlng.lng })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
this.handleEvent("point_detail", (detail) => {
|
||||||
if (detail) {
|
if (detail) {
|
||||||
this.showRangeCircles(detail)
|
const merged = { ...detail, ...this.bandInfo }
|
||||||
this.detailPopup
|
this.detailPopup
|
||||||
.setLatLng([detail.lat, detail.lon])
|
.setLatLng([detail.lat, detail.lon])
|
||||||
.setContent(buildPopupHTML(detail))
|
.setContent(buildPopupHTML(merged))
|
||||||
.openOn(this.map)
|
.openOn(this.map)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -327,6 +334,13 @@ export const PropagationMap = {
|
||||||
this.gridData.set(key, s)
|
this.gridData.set(key, s)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Pre-calculate RGBA strings for scores 0-100
|
||||||
|
const colorCache = new Array(101)
|
||||||
|
for (let i = 0; i <= 100; i++) {
|
||||||
|
const c = this.scoreColorRGB(i)
|
||||||
|
colorCache[i] = `rgba(${c.r},${c.g},${c.b},0.55)`
|
||||||
|
}
|
||||||
|
|
||||||
const self = this
|
const self = this
|
||||||
this.scoreOverlay = L.GridLayer.extend({
|
this.scoreOverlay = L.GridLayer.extend({
|
||||||
createTile(coords) {
|
createTile(coords) {
|
||||||
|
|
@ -345,15 +359,20 @@ export const PropagationMap = {
|
||||||
const lonPerPx = (se.lng - nw.lng) / size.x
|
const lonPerPx = (se.lng - nw.lng) / size.x
|
||||||
const cellPxX = Math.max(1, Math.ceil(step / lonPerPx))
|
const cellPxX = Math.max(1, Math.ceil(step / lonPerPx))
|
||||||
const cellPxY = Math.max(1, Math.ceil(step / Math.abs(latPerPx)))
|
const cellPxY = Math.max(1, Math.ceil(step / Math.abs(latPerPx)))
|
||||||
|
const absLatPerPx = Math.abs(latPerPx)
|
||||||
|
|
||||||
|
let lastColor = null
|
||||||
for (let py = 0; py < size.y; py += cellPxY) {
|
for (let py = 0; py < size.y; py += cellPxY) {
|
||||||
|
const lat = nw.lat - py * absLatPerPx
|
||||||
for (let px = 0; px < size.x; px += cellPxX) {
|
for (let px = 0; px < size.x; px += cellPxX) {
|
||||||
const lat = nw.lat - py * Math.abs(latPerPx)
|
|
||||||
const lon = nw.lng + px * lonPerPx
|
const lon = nw.lng + px * lonPerPx
|
||||||
const score = self.interpolateScore(lat, lon, step)
|
const score = self.interpolateScore(lat, lon, step)
|
||||||
if (score !== null) {
|
if (score !== null) {
|
||||||
const color = self.scoreColorRGB(score)
|
const color = colorCache[Math.max(0, Math.min(100, Math.round(score)))]
|
||||||
ctx.fillStyle = `rgba(${color.r},${color.g},${color.b},0.55)`
|
if (color !== lastColor) {
|
||||||
|
ctx.fillStyle = color
|
||||||
|
lastColor = color
|
||||||
|
}
|
||||||
ctx.fillRect(px, py, cellPxX, cellPxY)
|
ctx.fillRect(px, py, cellPxX, cellPxY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,11 @@ defmodule MicrowavepropWeb.MapLive do
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_event("point_detail", %{"lat" => lat, "lon" => lon}, socket) do
|
||||||
|
detail = Propagation.point_detail(socket.assigns.selected_band, lat, lon)
|
||||||
|
{:noreply, push_event(socket, "point_detail", detail || %{})}
|
||||||
|
end
|
||||||
|
|
||||||
def handle_event("map_bounds", bounds, socket) do
|
def handle_event("map_bounds", bounds, socket) do
|
||||||
scores = Propagation.latest_scores(socket.assigns.selected_band, bounds)
|
scores = Propagation.latest_scores(socket.assigns.selected_band, bounds)
|
||||||
|
|
||||||
|
|
@ -121,7 +126,7 @@ defmodule MicrowavepropWeb.MapLive do
|
||||||
id="propagation-map"
|
id="propagation-map"
|
||||||
phx-hook="PropagationMap"
|
phx-hook="PropagationMap"
|
||||||
phx-update="ignore"
|
phx-update="ignore"
|
||||||
data-scores={Jason.encode!(@scores)}
|
data-scores={@initial_scores_json}
|
||||||
data-band-info={Jason.encode!(band_info(@selected_band))}
|
data-band-info={Jason.encode!(band_info(@selected_band))}
|
||||||
class="absolute inset-0 z-0"
|
class="absolute inset-0 z-0"
|
||||||
>
|
>
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue