prop/assets/js/propagation_map_hook.js

90 lines
2.7 KiB
JavaScript

export const PropagationMap = {
mounted() {
// Center on DFW airport, ~500 mile radius
this.map = L.map(this.el, {
center: [32.897, -97.038],
zoom: 7,
minZoom: 4,
maxZoom: 10
})
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "© OpenStreetMap contributors",
maxZoom: 19
}).addTo(this.map)
this.scoreLayer = L.layerGroup().addTo(this.map)
this.colorScale = [
{ min: 80, color: "#00ffa3" },
{ min: 65, color: "#7dffd4" },
{ min: 50, color: "#ffe566" },
{ min: 33, color: "#ff9044" },
{ min: 0, color: "#ff4f4f" }
]
const initialScores = JSON.parse(this.el.dataset.scores || "[]")
this.renderScores(initialScores)
this.handleEvent("update_scores", ({ scores }) => {
this.renderScores(scores)
})
const legend = L.control({ position: "bottomright" })
legend.onAdd = () => {
const div = L.DomUtil.create("div")
div.style.cssText = "background:#fff;color:#333;padding:10px 14px;border-radius:8px;box-shadow:0 2px 8px rgba(0,0,0,0.3);font-size:13px;line-height:2;"
const rows = [
["#00ffa3", "Excellent (80-100)"],
["#7dffd4", "Good (65-79)"],
["#ffe566", "Marginal (50-64)"],
["#ff9044", "Poor (33-49)"],
["#ff4f4f", "Negligible (0-32)"]
]
div.innerHTML = "<strong style='color:#333;'>Propagation</strong><br>" +
rows.map(([c, label]) =>
`<span style="background:${c};width:14px;height:14px;display:inline-block;border-radius:50%;vertical-align:middle;margin-right:6px;border:1px solid rgba(0,0,0,0.15);"></span><span style="color:#333;">${label}</span>`
).join("<br>")
return div
}
legend.addTo(this.map)
},
renderScores(scores) {
this.scoreLayer.clearLayers()
scores.forEach(({ lat, lon, score }) => {
const color = this.scoreColor(score)
const circle = L.circleMarker([lat, lon], {
radius: 6,
fillColor: color,
fillOpacity: 0.7,
color: color,
weight: 0
})
circle.bindPopup(
`<strong>Score: ${score}/100</strong><br>${this.scoreTier(score)}<br>` +
`<small>${lat.toFixed(3)}\u00b0N, ${Math.abs(lon).toFixed(3)}\u00b0W</small>`
)
this.scoreLayer.addLayer(circle)
})
},
scoreColor(score) {
for (const tier of this.colorScale) {
if (score >= tier.min) return tier.color
}
return "#ff4f4f"
},
scoreTier(score) {
if (score >= 80) return "EXCELLENT"
if (score >= 65) return "GOOD"
if (score >= 50) return "MARGINAL"
if (score >= 33) return "POOR"
return "NEGLIGIBLE"
},
destroyed() {
if (this.map) this.map.remove()
}
}