Auto-download missing SRTM tiles, treat water areas as 0m elevation
SRTM profiles no longer abort on missing tiles — water/ocean areas return 0m elevation instead of failing the entire profile and falling back to the rate-limited API. Elevation client and viewshed now pass download: true to auto-fetch missing tiles from S3. NFS mount changed to writable so downloaded tiles persist across pods.
This commit is contained in:
parent
80d1169f71
commit
e74adf0036
6 changed files with 32 additions and 56 deletions
|
|
@ -105,10 +105,8 @@ spec:
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: srtm-data
|
- name: srtm-data
|
||||||
mountPath: /srtm
|
mountPath: /srtm
|
||||||
readOnly: true
|
|
||||||
volumes:
|
volumes:
|
||||||
- name: srtm-data
|
- name: srtm-data
|
||||||
nfs:
|
nfs:
|
||||||
server: 204.110.191.8
|
server: 204.110.191.8
|
||||||
path: /data/srtm
|
path: /data/srtm
|
||||||
readOnly: true
|
|
||||||
|
|
|
||||||
|
|
@ -16,14 +16,8 @@ defmodule Microwaveprop.Terrain.ElevationClient do
|
||||||
fetch_elevation_profile_api(lat1, lon1, lat2, lon2, n)
|
fetch_elevation_profile_api(lat1, lon1, lat2, lon2, n)
|
||||||
|
|
||||||
tiles_dir ->
|
tiles_dir ->
|
||||||
case Srtm.fetch_elevation_profile(lat1, lon1, lat2, lon2, tiles_dir, n, opts) do
|
srtm_opts = Keyword.merge([download: true], opts)
|
||||||
{:ok, _profile} = ok ->
|
Srtm.fetch_elevation_profile(lat1, lon1, lat2, lon2, tiles_dir, n, srtm_opts)
|
||||||
ok
|
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
Logger.warning("SRTM failed (#{inspect(reason)}), falling back to API")
|
|
||||||
fetch_elevation_profile_api(lat1, lon1, lat2, lon2, n)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,26 +84,19 @@ defmodule Microwaveprop.Terrain.Srtm do
|
||||||
pts = sample_path(lat1, lon1, lat2, lon2, n)
|
pts = sample_path(lat1, lon1, lat2, lon2, n)
|
||||||
dist_km = haversine_km(lat1, lon1, lat2, lon2)
|
dist_km = haversine_km(lat1, lon1, lat2, lon2)
|
||||||
|
|
||||||
results =
|
profile =
|
||||||
Enum.reduce_while(pts, {:ok, []}, fn pt, {:ok, acc} ->
|
Enum.map(pts, fn pt ->
|
||||||
case lookup(pt.lat, pt.lon, tiles_dir, opts) do
|
elev =
|
||||||
{:ok, elev} ->
|
case lookup(pt.lat, pt.lon, tiles_dir, opts) do
|
||||||
entry = %{
|
{:ok, elev} -> elev
|
||||||
lat: pt.lat,
|
# Missing tile (water/ocean) or void value — treat as sea level
|
||||||
lon: pt.lon,
|
{:error, _} -> 0
|
||||||
d: pt.d,
|
end
|
||||||
elev: elev,
|
|
||||||
dist_km: pt.d * dist_km
|
|
||||||
}
|
|
||||||
|
|
||||||
{:cont, {:ok, acc ++ [entry]}}
|
%{lat: pt.lat, lon: pt.lon, d: pt.d, elev: elev, dist_km: pt.d * dist_km}
|
||||||
|
|
||||||
{:error, reason} ->
|
|
||||||
{:halt, {:error, reason}}
|
|
||||||
end
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
results
|
{:ok, profile}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp read_elevation(fd, lat, lon) do
|
defp read_elevation(fd, lat, lon) do
|
||||||
|
|
|
||||||
|
|
@ -151,25 +151,21 @@ defmodule Microwaveprop.Terrain.Viewshed do
|
||||||
terrain_check_km = min(radio_horizon_km(ant_height_m) * 2.0, max_range_km)
|
terrain_check_km = min(radio_horizon_km(ant_height_m) * 2.0, max_range_km)
|
||||||
{end_lat, end_lon} = destination_point(origin_lat, origin_lon, bearing, terrain_check_km)
|
{end_lat, end_lon} = destination_point(origin_lat, origin_lon, bearing, terrain_check_km)
|
||||||
|
|
||||||
case Srtm.fetch_elevation_profile(origin_lat, origin_lon, end_lat, end_lon, tiles_dir) do
|
{:ok, profile} =
|
||||||
{:ok, profile} ->
|
Srtm.fetch_elevation_profile(origin_lat, origin_lon, end_lat, end_lon, tiles_dir, 64, download: true)
|
||||||
analysis =
|
|
||||||
TerrainAnalysis.analyse(profile, terrain_check_km, freq_ghz, ant_ht_a: ant_height_m, ant_ht_b: ant_height_m)
|
|
||||||
|
|
||||||
reach_km = effective_reach_km(analysis, max_range_km, score)
|
analysis =
|
||||||
{reach_lat, reach_lon} = destination_point(origin_lat, origin_lon, bearing, reach_km)
|
TerrainAnalysis.analyse(profile, terrain_check_km, freq_ghz, ant_ht_a: ant_height_m, ant_ht_b: ant_height_m)
|
||||||
|
|
||||||
%{
|
reach_km = effective_reach_km(analysis, max_range_km, score)
|
||||||
bearing: bearing,
|
{reach_lat, reach_lon} = destination_point(origin_lat, origin_lon, bearing, reach_km)
|
||||||
reach_km: reach_km,
|
|
||||||
lat: reach_lat,
|
|
||||||
lon: reach_lon
|
|
||||||
}
|
|
||||||
|
|
||||||
{:error, _} ->
|
%{
|
||||||
{end_lat2, end_lon2} = destination_point(origin_lat, origin_lon, bearing, max_range_km)
|
bearing: bearing,
|
||||||
%{bearing: bearing, reach_km: max_range_km, lat: end_lat2, lon: end_lon2}
|
reach_km: reach_km,
|
||||||
end
|
lat: reach_lat,
|
||||||
|
lon: reach_lon
|
||||||
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Radio horizon distance in km for a given antenna height (K=4/3 atmosphere)
|
# Radio horizon distance in km for a given antenna height (K=4/3 atmosphere)
|
||||||
|
|
|
||||||
|
|
@ -148,21 +148,14 @@ defmodule Microwaveprop.Terrain.ElevationClientSrtmTest do
|
||||||
end
|
end
|
||||||
|
|
||||||
@tag :srtm
|
@tag :srtm
|
||||||
test "falls back to API when SRTM tiles missing" do
|
test "returns zero elevation when SRTM tiles missing (no API fallback)" do
|
||||||
Application.put_env(:microwaveprop, :srtm_tiles_dir, "/nonexistent/tiles")
|
Application.put_env(:microwaveprop, :srtm_tiles_dir, "/nonexistent/tiles")
|
||||||
|
|
||||||
Req.Test.stub(ElevationClient, fn conn ->
|
assert {:ok, profile} =
|
||||||
Req.Test.json(conn, %{"elevation" => [200.0, 250.0, 180.0]})
|
ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 2)
|
||||||
end)
|
|
||||||
|
|
||||||
{result, _log} =
|
|
||||||
with_log(fn ->
|
|
||||||
ElevationClient.fetch_elevation_profile(32.9, -97.0, 30.3, -97.7, 2)
|
|
||||||
end)
|
|
||||||
|
|
||||||
assert {:ok, profile} = result
|
|
||||||
assert length(profile) == 3
|
assert length(profile) == 3
|
||||||
assert hd(profile).elev == 200.0
|
assert Enum.all?(profile, fn p -> p.elev == 0 end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -190,9 +190,11 @@ defmodule Microwaveprop.Terrain.SrtmTest do
|
||||||
assert last.dist_km > 0
|
assert last.dist_km > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
test "returns error when tiles dir does not exist" do
|
test "returns zero elevation when tiles dir does not exist" do
|
||||||
assert {:error, _} =
|
assert {:ok, profile} =
|
||||||
Srtm.fetch_elevation_profile(32.78, -96.8, 32.5, -96.5, "/nonexistent", 4)
|
Srtm.fetch_elevation_profile(32.78, -96.8, 32.5, -96.5, "/nonexistent", 4)
|
||||||
|
|
||||||
|
assert Enum.all?(profile, fn p -> p.elev == 0 end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue