Commit graph

1615 commits

Author SHA1 Message Date
FluxCD
5094ea0837 chore: update prop image to git.mcintire.me/graham/prop:main-1777227067-ca142d5 [skip ci] 2026-04-26 18:15:24 +00:00
ca142d5091
feat(rover-locations): show 10-char Maidenhead grid (computed, never stored) 2026-04-26 13:10:49 -05:00
140351c0f6
feat(rover-locations): add /rover-locations page with CRUD
New globally-shared list of rover-friendly (or off-limits) parking
spots. Anyone can view; logged-in users can add new locations and
edit/delete their own. Each location stores lat/lon, free-text notes,
and a status enum (:ideal | :off_limits).

  * Microwaveprop.Rover.Location schema + migration
  * Rover.list_locations/0, create_location/2, update_location/3,
    delete_location/2 (mutations require User; ownership-scoped)
  * /rover-locations LiveView in the public live_session — Add button
    is disabled for anonymous visitors, Edit/Delete buttons render
    only on the user's own entries
  * Tests for context + LiveView covering anonymous read, owner-only
    edit/delete, and form submission
2026-04-26 13:08:27 -05:00
FluxCD
ac98d35330 chore: update prop image to git.mcintire.me/graham/prop:main-1777226288-7105ac6 [skip ci] 2026-04-26 18:02:20 +00:00
7105ac61df
feat(rover): add canopy clutter penalty + Canopy.BulkFetch downloader
Rover ranking now applies a per-cell tree-canopy clutter penalty
(1 dB / 8 m, capped at 4 dB) so cells in dense forests get down-ranked
even when their per-station path clearance survives. PathTerrain
already accounts for trees ON the link path; this surfaces "I'm in a
forest, every direction is foliage."

Canopy.BulkFetch downloads Lang et al. 2020 10 m global canopy-height
3-degree COGs and slices each into 1° × 1° uint8 .canopy tiles via
gdal_translate, the runtime image now ships gdal-bin. Run on prod via:

  bin/microwaveprop rpc 'Microwaveprop.Canopy.BulkFetch.run(32.8, -97.0, 200)'

Tile cache lives under /data/canopy/. Already-sliced tiles are skipped.
2026-04-26 12:57:50 -05:00
FluxCD
1a85a5f540 chore: update prop image to git.mcintire.me/graham/prop:main-1777225924-4f647e7 [skip ci] 2026-04-26 17:55:18 +00:00
4f647e7894
feat(canopy): add tree-canopy height layer for path obstruction analysis
Adds Microwaveprop.Canopy module that reads 1° × 1° uint8 per-degree
canopy-height tiles (mirrors SRTM .hgt naming/layout, 30 m resolution).
Returns 0 m for areas with no tile on disk so the lookup is safe to
thread through the existing path-clearance pipeline before any data
lands.

Wires canopy into:
  - PathTerrain.obstacle_top: per-sample blocker = ground +
    max(building_m, canopy_m), so the rover ranks paths through forests
    as obstructed even when no buildings sit on the line
  - CandidateDetail profile: each sample now carries canopy_m alongside
    building_m
  - Rover-detail SVG: green "trees" polygon stacked on terrain, under
    the red building polygon
  - Path calculator elevation chart: green Tree Canopy dataset between
    terrain and buildings

Data prep (download Potapov 2019 / Lang 2022 GeoTIFF, slice to per-degree
uint8 tiles, drop in /data/canopy/) is a separate one-shot operation —
without tiles, lookups return 0 and the existing behavior is unchanged.
2026-04-26 12:51:48 -05:00
FluxCD
87d5175c6f chore: update prop image to git.mcintire.me/graham/prop:main-1777224612-aaf3115 [skip ci] 2026-04-26 17:34:04 +00:00
aaf3115fd6
feat(path): show buildings stacked on terrain in path elevation profile
Path calculator now queries Microsoft Building Footprints for the
tallest structure within 80m of each profile sample and renders a red
"Buildings" dataset on top of the terrain layer in the elevation chart.

Also normalize on-disk grid HRRR cells to include the legacy
:min_refractivity_gradient / :surface_refractivity / :ducting_detected
keys (mapped from :native_min_gradient and :duct_count). Path-calculator
crashed in prod with KeyError when a path's HRRR points came from the
new on-disk grid format instead of the DB profile shape.
2026-04-26 12:29:53 -05:00
FluxCD
031db7befc chore: update prop image to git.mcintire.me/graham/prop:main-1777223156-82b515e [skip ci] 2026-04-26 17:09:51 +00:00
82b515e88d
feat(rover): include step number/total in Calculate progress label 2026-04-26 12:05:32 -05:00
9f8c4d3b36
feat(rover): show current pipeline step next to Calculate spinner
Compute.run now accepts a `progress` callback and reports human-readable
labels before each pipeline step (loading grid, looking up elevation,
checking road access, scoring cells, etc.). RoverLive feeds it via
send/2 to itself and renders the latest label as small text to the left
of the Calculate button while it's running.
2026-04-26 12:02:40 -05:00
FluxCD
88d785be50 chore: update prop image to git.mcintire.me/graham/prop:main-1777222350-5e1155f [skip ci] 2026-04-26 16:55:48 +00:00
FluxCD
5d9e9e1084 chore: update prop image to git.mcintire.me/graham/prop:main-1777222118-5a4129f [skip ci] 2026-04-26 16:52:29 +00:00
5e1155f9da
feat(rover): add ESRI satellite imagery as base-layer option 2026-04-26 11:52:12 -05:00
5a4129ffca
fix(rover): show building height tooltip in feet 2026-04-26 11:48:15 -05:00
b97af8a5ce
feat(rover): show buildings stacked on terrain in path profile
Each profile sample now carries the tallest building height within 80m
of the path point. The candidate-detail SVG renders these as a red
polygon stacked on top of the terrain so blockage from buildings is
visible alongside ridges. max_obstacle_m now reflects building tops too,
so the clearance label downgrades when buildings sit on the link.
2026-04-26 11:47:24 -05:00
FluxCD
3b8d87c356 chore: update prop image to git.mcintire.me/graham/prop:main-1777220031-2f060b4 [skip ci] 2026-04-26 16:17:21 +00:00
2f060b4371
feat(rover): penalize cells surrounded by tall buildings (clutter)
Adds a per-cell building-clutter penalty so the algorithm down-ranks
spots in built environments where buildings on multiple sides
scatter/block signal regardless of which station you're aiming at.

Penalty = max_height_within_75m / 5 dB, capped at 6 dB. Path-clearance
already accounts for buildings ON the link path; this is the
"surrounded by stuff" signal.
2026-04-26 11:13:31 -05:00
FluxCD
6710140b72 chore: update prop image to git.mcintire.me/graham/prop:main-1777218902-a290c99 [skip ci] 2026-04-26 15:58:15 +00:00
a290c99184
fix(buildings): use explicit step in descending range in encode_quadkey
zoom..1 without a step raises a runtime warning in Elixir 1.19 and was
causing the BulkFetch Task to crash immediately. Changed to zoom..1//-1.
2026-04-26 10:54:44 -05:00
FluxCD
b17ff838ca chore: update prop image to git.mcintire.me/graham/prop:main-1777218305-0f15e99 [skip ci] 2026-04-26 15:49:03 +00:00
0f15e99fb9
feat(buildings): integrate MS footprints into path clearance + map render
Phase 2/3/5 of the building-blockage support:

* Parser streams csv.gz tiles into compact records (centroid, radius,
  height) — keeps RAM ~32 B per polygon, drops -1.0-height entries.
* Index buckets records into 0.01° (~1 km) grid cells in a public ETS
  table for concurrent reads; max_height_near/3 and records_near/3
  scan only the 9 nearest buckets.
* Loader lazily parses any cached quadkey before a Calculate so the
  scorer is terrain-only when tiles aren't on disk yet.
* Rover.PathTerrain now treats each path-sample obstacle as
  terrain_elev + max_local_building_height, so blocked LOS through
  recent construction actually reduces clearance.
* Selecting a candidate pushes a candidate_buildings event with the
  buildings near each rover→station path; the hook renders them as
  height-coloured circles (yellow <10 m, orange 10-30 m, red >=30 m)
  with a tooltip showing height in meters.
2026-04-26 10:44:46 -05:00
FluxCD
a150e02cd0 chore: update prop image to git.mcintire.me/graham/prop:main-1777217856-021608d [skip ci] 2026-04-26 15:41:01 +00:00
021608dc90
feat(buildings): bulk-prefetch tiles via BulkFetch.run/4 (works in prod)
Mix tasks don't run in production, so the orchestration lives in
Microwaveprop.Buildings.BulkFetch — callable from an attached iex on
the prod pod via:

    iex> Microwaveprop.Buildings.BulkFetch.run(32.8, -97.0, 500)

Default 500 mi radius around DFW yields ~440 candidate quadkeys at
zoom 9; only those present in the MS dataset index (typically a few
hundred over CONUS) get downloaded. Concurrency 4, per-task timeout
3 min, results logged with downloaded/cached/failed/missing counts.
The mix.task is kept as a dev-only thin wrapper.
2026-04-26 10:37:13 -05:00
5a97776fd5
feat(buildings): MS Global ML Building Footprints quadkey + tile fetcher
Phase 1 of building-blockage support: implements quadkey math (Bing/MS
tile encoding), looks up the MS dataset-links index for UnitedStates,
and downloads csv.gz quadkey tiles to /data/buildings (overridable via
:buildings_cache_dir). Index is cached 24h; tiles are written once and
reused. No path-analysis wiring yet — that's the next slice.
2026-04-26 10:33:25 -05:00
FluxCD
ff7f369e57 chore: update prop image to git.mcintire.me/graham/prop:main-1777215438-2f46d49 [skip ci] 2026-04-26 15:00:50 +00:00
2f46d49016
feat(rover): clear selected candidate + path lines on recalculate 2026-04-26 09:56:55 -05:00
556967db5a
feat(grid): show full Maidenhead label at subsquare zoom (em13sf, not sf) 2026-04-26 09:54:08 -05:00
FluxCD
b2aec7e8c8 chore: update prop image to git.mcintire.me/graham/prop:main-1777213888-3b1a13b [skip ci] 2026-04-26 14:34:35 +00:00
3b1a13b0f7
feat(rover): make candidate strip scroll horizontally + wheel-to-h-scroll 2026-04-26 09:31:10 -05:00
12e0106b7b
feat(rover): drop drive-time from candidate cards and detail header 2026-04-26 09:29:27 -05:00
535fda7cce
feat(rover): default Maidenhead grid overlay to on 2026-04-26 09:27:33 -05:00
7f77939fa4
feat(rover): add Maidenhead grid-square overlay (toggle in layers control) 2026-04-26 09:26:59 -05:00
af2ab3c969
fix(rover): bound Overpass with hard 8s deadline + 6h ETS cache by bbox
Prod logs showed road_proximity took 186 s on a single Calculate.
Req's receive_timeout only catches TCP idle, not slow streaming, so a
trickling Overpass response could not be cancelled. Wrap the call in a
Task.yield/shutdown deadline and cache successful results by 0.05°-rounded
bbox so adjacent Calculates skip Overpass entirely.
2026-04-26 09:25:08 -05:00
FluxCD
6a79458ea9 chore: update prop image to git.mcintire.me/graham/prop:main-1777212762-7d80bab [skip ci] 2026-04-26 14:16:11 +00:00
7d80babb18
feat(rover): snap top candidates to SRTM hilltops + add hillshade overlay 2026-04-26 09:12:24 -05:00
FluxCD
3c877b8b8e chore: update prop image to git.mcintire.me/graham/prop:main-1777212183-2f35005 [skip ci] 2026-04-26 14:10:09 +00:00
2f3500515a
feat(rover): overlay LOS line + label profile rover→station endpoints 2026-04-26 09:02:07 -05:00
FluxCD
ea502ee9e7 chore: update prop image to git.mcintire.me/graham/prop:main-1777158633-0d2cfb6 [skip ci] 2026-04-25 23:18:38 +00:00
0d2cfb68f1
feat(rover): show aim heading + path lines for each fixed station
- Detail panel now reads as an aiming card: "Aim 220° (SW) · clearance +12 ft"
  with the precise bearing in degrees, plus rover/obstacle elev on a second line
- Elevation profile labels swapped from min/max-of-profile to start (rover) and
  end (station) elevations, since their positions in the chart now match
- Map draws a dashed indigo polyline from the candidate to each selected
  station when a candidate is opened; cleared when the panel closes
2026-04-25 18:10:15 -05:00
4bfcff4e32
perf(rover): tighten Overpass timeout and log per-step compute timing
Calculate occasionally hung in production for ~30s. Adds per-phase
timing logs to Compute.run so the slow step is visible in pod logs,
and trims the Overpass road-proximity request to a 10s server-side
timeout / 12s receive timeout (no Req retries) so a slow road API
fails soft to no road penalty instead of stalling Calculate.
2026-04-25 18:05:46 -05:00
10cbd8dd48
fix(remote_ip): log IPv6 in canonical hex via :inet.ntoa
IPv6 segments were being printed in decimal (e.g. 10772:1985:1024:45::1
for 2a14:7c1:400:2d::1), which made request_id logs unreadable.
2026-04-25 18:05:46 -05:00
FluxCD
5c178ef4b7 chore: update prop image to git.mcintire.me/graham/prop:main-1777157172-eac817c [skip ci] 2026-04-25 22:56:35 +00:00
eac817cd57
feat(rover): prefer broad hilltops near roads as ideal candidates
- Local prominence: each cell's elevation vs. its 8-neighbor ring
  (~1.3 km radius) feeds a small additive bonus so broad hilltops
  rank above isolated SRTM voxels
- Road proximity: one Overpass call per Calculate fetches drivable
  ways inside the bbox; cells beyond ~0.5 km of any road get a
  light dB penalty, gated off in tests
2026-04-25 17:45:48 -05:00
4611926b8d
feat(rover): per-station detail panel with elevation profile + 'Ideal locations' label 2026-04-25 17:41:48 -05:00
FluxCD
819bf2e27d chore: update prop image to git.mcintire.me/graham/prop:main-1777156652-6462acf [skip ci] 2026-04-25 22:40:28 +00:00
6462acffc6
feat(rover): default 50mi max distance and frame map to that radius 2026-04-25 17:37:17 -05:00
083c393bc8
fix(rover): clip heatmap to drive-radius circle on render 2026-04-25 17:35:44 -05:00
FluxCD
c1eabb788f chore: update prop image to git.mcintire.me/graham/prop:main-1777156190-40cbb40 [skip ci] 2026-04-25 22:33:25 +00:00