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.
40 lines
1.8 KiB
Elixir
40 lines
1.8 KiB
Elixir
defmodule Microwaveprop.Buildings.ParserTest do
|
|
use ExUnit.Case, async: true
|
|
|
|
alias Microwaveprop.Buildings.Parser
|
|
|
|
@sample_geojsonl """
|
|
{"type": "Feature", "properties": {"height": 5.5, "confidence": -1.0}, "geometry": {"type": "Polygon", "coordinates": [[[-97.736, 32.221], [-97.736, 32.222], [-97.735, 32.222], [-97.735, 32.221], [-97.736, 32.221]]]}}
|
|
{"type": "Feature", "properties": {"height": 12.0, "confidence": 0.9}, "geometry": {"type": "Polygon", "coordinates": [[[-97.0, 32.5], [-97.0, 32.501], [-96.999, 32.501], [-96.999, 32.5], [-97.0, 32.5]]]}}
|
|
{"type": "Feature", "properties": {"height": -1.0, "confidence": -1.0}, "geometry": {"type": "Polygon", "coordinates": [[[-97.5, 32.0], [-97.5, 32.001], [-97.499, 32.001], [-97.499, 32.0], [-97.5, 32.0]]]}}
|
|
"""
|
|
|
|
setup do
|
|
path = Path.join(System.tmp_dir!(), "buildings_parser_#{:erlang.unique_integer([:positive])}.csv.gz")
|
|
gz = :zlib.gzip(@sample_geojsonl)
|
|
File.write!(path, gz)
|
|
on_exit(fn -> File.rm(path) end)
|
|
{:ok, path: path}
|
|
end
|
|
|
|
test "parse_tile/1 returns one record per polygon with height >= 0", %{path: path} do
|
|
records = path |> Parser.parse_tile() |> Enum.to_list()
|
|
|
|
assert length(records) == 2
|
|
assert Enum.all?(records, fn r -> r.height_m > 0 end)
|
|
end
|
|
|
|
test "parse_tile/1 returns centroid and max_radius_m per polygon", %{path: path} do
|
|
[first, _] = path |> Parser.parse_tile() |> Enum.to_list()
|
|
|
|
assert_in_delta first.centroid_lat, 32.2215, 0.001
|
|
assert_in_delta first.centroid_lon, -97.7355, 0.001
|
|
assert first.max_radius_m > 0
|
|
assert first.height_m == 5.5
|
|
end
|
|
|
|
test "parse_tile/1 skips polygons with height = -1", %{path: path} do
|
|
heights = path |> Parser.parse_tile() |> Enum.map(& &1.height_m)
|
|
refute -1.0 in heights
|
|
end
|
|
end
|