towerops/priv/repo/migrations/20260506220000_add_height_tiers_to_coverages.exs
Graham McIntire 434f5782d0 feat(coverage): multi-SM-height compute + cnHeat-style height slider
Vertical 'Height above clutter' slider on the left of the map now
matches cnHeat's experience: pick an SM install height, the heatmap
re-renders to show only the coverage achievable from that height.

Backend
- New JSONB height_tiers column on coverages keyed by string metres
  → relative PNG path.
- CoverageWorker runs compute_pixels once per tier in
  [1.83, 3.05, 4.57, 6.10, 9.14, 12.19] m (~6/10/15/20/30/40 ft)
  by varying receiver_height_m on a copied struct. Each tier
  writes its own raster + PNG via Raster.write/4 with a per-height
  filename suffix; the default 3.05 m tier also populates the
  legacy raster_path / png_path so existing show pages keep
  working.

Frontend
- Vertical range slider (writing-mode: vertical-lr) anchored
  centre-left. Step picks one of HEIGHT_TIERS_FT; label updates
  to '6 ft'..'40 ft'.
- pngForCoverage/2 picks the closest available tier from the
  payload's height_tiers map, falling back to the original png
  for coverages not yet recomputed.
- On slider change the JS swaps each L.imageOverlay's URL via
  setUrl, drops the cached pixel data, and re-decodes so the RSSI
  filter still applies. coverage_hooks chunk: 20 KB → 22 KB.
2026-05-06 16:38:07 -05:00

12 lines
400 B
Elixir

defmodule Towerops.Repo.Migrations.AddHeightTiersToCoverages do
use Ecto.Migration
def change do
alter table(:coverages) do
# Map of receiver-height-in-metres → relative PNG path written
# by the worker. Empty for coverages that haven't been recomputed
# since the multi-height pipeline shipped.
add :height_tiers, :map, default: %{}, null: false
end
end
end