Commit 99a57d1
authored
* Add an interactive viewshed from a clicked map point (#1815)
Right-click the map -> Viewshed from here, at 2/5/15 km. No DEM to find,
download or load first: the terrain the map is already rendering is the input.
Not routed through the Whitebox viewshed, despite the issue suggesting it. That
tool consumes a DEM raster file *and* a station-point vector file and emits a
raster file, so from a clicked point it is three format round-trips before any
analysis runs -- and it still requires the user to have a DEM in hand, which is
the data-prep step the issue is about removing. The line-of-sight is ~60 lines
on a grid we already hold in memory, and computing it directly gives exact
control over observer height and radius, both of which the issue calls for. The
Whitebox tool is untouched and remains the rigorous DEM-in-hand option.
Two halves, deliberately separable so the geometry is testable without a
network: assembleTerrainDem fetches and decodes the Terrarium RGB tiles the
terrain control already uses into one elevation grid, and computeViewshed walks
a ray to every cell tracking the maximum slope seen so far.
Tiles that fail to load leave their cells at 0 rather than aborting: a viewshed
with one missing tile is degraded but still useful, while a hard failure makes a
transient network error look like a broken feature.
The zoom is chosen from the radius to land near 512 cells across, so both the
fetch and the O(n^2*sqrt(n)) walk stay interactive, and the radius is clamped to
50 km so one click cannot pull a continent of tiles.
The result is an "image" layer -- a translucent wash pinned by corner
coordinates -- reusing the overlay path the Raster Georeferencer established, so
it gets opacity, ordering, zoom-to and removal for free with no new layer type.
A PNG data URL rather than a blob URL, since the layer is saved with the project
and a blob URL would be dead on reopen.
Earth curvature and refraction are not modelled; documented on the function,
since at the 50 km cap the curvature drop reaches ~180 m and that matters for a
radio study even though it does not for "what can I see from this overlook".
* Address review feedback on the interactive viewshed
- decodeTile now reports the decoded tile's dimensions, and a tile served at a
size other than the mosaic assumes is skipped rather than copied at the wrong
stride, which would have sheared the elevations.
- Tile fetches carry a 15s timeout. A hung request would otherwise stall the
whole assembly behind Promise.all with no recovery, since fetch has none by
default. Composed with the caller's signal via AbortSignal.any where present.
- Reject out-of-range positions up front instead of leaving them to fail as 404s
partway through: beyond ~85 degrees the tile grid is undefined, and a square
straddling the antimeridian produces a negative tile x.
- Remove the dead visibleCells === 0 branch. computeViewshed always marks the
observer's own cell, so the count is at least 1 by construction.
- The radius labels went through hardcoded "km"/"m"; they now format for the
active locale and take the unit from the catalog, matching how
formatBufferDistance labels the buffer presets in the same menu.
- Export AssembleTerrainDemOptions alongside the function it parameterizes.
* Clamp the viewshed radius on both bounds
run-viewshed clamped only the ceiling, but assembleTerrainDem floors to
MIN_VIEWSHED_RADIUS_METERS internally. A radius below the floor would therefore
build the DEM at the floor while computeViewshed culled against the smaller
requested value, so the analysed square and the visibility limit disagreed. Not
reachable from the menu presets, but the runner is exported.
* Reuse the existing unit catalog for the viewshed radius labels
quickAnalysis.unit.kilometers/meters already existed and are what
formatBufferDistance uses a few lines below, so the new unitKilometers/
unitMeters keys were 36 redundant strings across 18 catalogs. Removed them and
switched to the existing lookup. Also restored alphabetical order in the icon
import.
* Address fourth review round on the viewshed
- Set metadata.bounds on the result layer. fitLayer resolves an extent from
geojson, then source/metadata bounds, then the live source's bounds -- an
ImageSource exposes only coordinates, so Zoom to layer silently did nothing,
contradicting this PR's claim that the image-layer path gets it for free.
- Report through the Quick Analysis banner instead of swallowing failures. The
terrain fetch takes seconds and can fail; a click with no feedback either way
reads as a broken menu item. setQuickAnalysisStatus is exported for actions
that are not registry tools.
- Correct the complexity claim: the walk is O(n^3) in the grid's side length,
not O(n^2*sqrt(n)).
- Icon import: Eye after Earth, which is where alphabetical order puts it.
* Move the viewshed off the main thread, and guard its banner writes
- computeViewshedAsync runs the line-of-sight walk on a one-shot Web Worker,
falling back to this thread where Workers do not exist (SSR, node:test, a
locked-down webview). The walk is O(n^3) in the grid's side length with no
yield points, so running it inline froze the tab for its whole duration --
several seconds at the 15km preset on modest hardware, which reads worse than
a network wait because nothing repaints. The DEM and the visibility grid are
plain typed arrays, so the round trip is one clone in and one transfer out.
Mirrors runToolOnWorker in wasm-convert.ts: one job per worker, terminated on
the terminal message, no timeout.
- The viewshed's banner writes now go through the same latestRun guard
runQuickAnalysis uses. setQuickAnalysisStatus let a slow viewshed resolving
after a faster action clobber that action's status -- exactly the "resurrect a
banner for a run the user has moved on from" case the counter exists to
prevent. Replaced with beginQuickAnalysisRun, which claims the run and returns
a setter bound to it.
* Fix the viewshed always reporting "no visible area"
decodeTile read bitmap.width/height *after* bitmap.close(), which zeroes them.
Every tile therefore decoded to a 0x0 grid, which the assembly loop then
discarded as the wrong size -- so `loaded` stayed 0, assembleTerrainDem returned
null, and every click reported "No visible area could be computed here."
Self-inflicted in 58be6da: that commit added the tile-dimension check a
reviewer asked for, and read the real dimensions from the bitmap to do it,
without noticing the read now happened after the close. The unit tests never
caught it because they stub the image and never exercise decodeTile.
Rather than just reordering the two lines, the RGBA-to-elevation step moves into
decodeTerrariumRgba, which takes width and height as arguments. A caller can no
longer size the output from an image it has already closed, so the ordering
hazard cannot come back. Verified end to end in a browser against real
Terrarium tiles: a 2km request over Zion now assembles a 263x263 DEM with an
observer ground elevation of 1240m, matching the visitor center.
* Address review feedback
- Frame the result after adding it, as every sibling quick action does. At the
larger radii the square can fall outside the viewport even though the clicked
point was on screen, so the banner flashed and cleared with nothing visible --
which reads as "nothing happened", worse than an error.
- Build the layer through the store's addImageOverlayLayer instead of
hand-rolling one. That action owns id generation, the default style,
source.type and the metadata merge, and is what the KML ground-overlay and
Georeferencer paths already use.
- Derive the worker's success payload from ViewshedResult rather than restating
its fields, so a field added there cannot be dropped by the rest-spread in
computeViewshedAsync without a type error.
- Interpolate the crop's rows in Web Mercator Y. Tile rows are uniform in
mercator space, not latitude, so interpolating in latitude skewed the crop
north-south by an amount that grows with latitude. The residual approximation
-- a single cell height for the whole grid -- is now documented alongside the
curvature note.
- Label the radii in the scale bar's unit system, so an imperial-preference user
no longer sees miles for buffers and kilometres for viewsheds in one submenu.
Memoized with explicit deps like the sibling formatDistance.
- Note on beginQuickAnalysisRun that it records no Processing History entry, so
the module docstring's claim about every quick action is not read as covering
a caller that is not a registry tool.
1 parent 55a1a18 commit 99a57d1
25 files changed
Lines changed: 1218 additions & 37 deletions
File tree
- apps/geolibre-desktop/src
- components/layout
- i18n/locales
- lib
- packages/processing/src
- tests
Lines changed: 93 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | | - | |
| 1 | + | |
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
| 21 | + | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| |||
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
| 39 | + | |
38 | 40 | | |
39 | 41 | | |
40 | 42 | | |
| 43 | + | |
41 | 44 | | |
42 | 45 | | |
43 | 46 | | |
| |||
197 | 200 | | |
198 | 201 | | |
199 | 202 | | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
200 | 233 | | |
201 | 234 | | |
202 | 235 | | |
203 | 236 | | |
204 | 237 | | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
205 | 283 | | |
206 | 284 | | |
207 | 285 | | |
| |||
361 | 439 | | |
362 | 440 | | |
363 | 441 | | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
| 449 | + | |
| 450 | + | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
364 | 456 | | |
365 | 457 | | |
366 | 458 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1900 | 1900 | | |
1901 | 1901 | | |
1902 | 1902 | | |
1903 | | - | |
1904 | | - | |
| 1903 | + | |
| 1904 | + | |
| 1905 | + | |
| 1906 | + | |
| 1907 | + | |
| 1908 | + | |
| 1909 | + | |
1905 | 1910 | | |
1906 | 1911 | | |
1907 | 1912 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1729 | 1729 | | |
1730 | 1730 | | |
1731 | 1731 | | |
1732 | | - | |
1733 | | - | |
| 1732 | + | |
| 1733 | + | |
| 1734 | + | |
| 1735 | + | |
| 1736 | + | |
| 1737 | + | |
| 1738 | + | |
1734 | 1739 | | |
1735 | 1740 | | |
1736 | 1741 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1729 | 1729 | | |
1730 | 1730 | | |
1731 | 1731 | | |
1732 | | - | |
1733 | | - | |
| 1732 | + | |
| 1733 | + | |
| 1734 | + | |
| 1735 | + | |
| 1736 | + | |
| 1737 | + | |
| 1738 | + | |
1734 | 1739 | | |
1735 | 1740 | | |
1736 | 1741 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1729 | 1729 | | |
1730 | 1730 | | |
1731 | 1731 | | |
1732 | | - | |
1733 | | - | |
| 1732 | + | |
| 1733 | + | |
| 1734 | + | |
| 1735 | + | |
| 1736 | + | |
| 1737 | + | |
| 1738 | + | |
1734 | 1739 | | |
1735 | 1740 | | |
1736 | 1741 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1729 | 1729 | | |
1730 | 1730 | | |
1731 | 1731 | | |
1732 | | - | |
1733 | | - | |
| 1732 | + | |
| 1733 | + | |
| 1734 | + | |
| 1735 | + | |
| 1736 | + | |
| 1737 | + | |
| 1738 | + | |
1734 | 1739 | | |
1735 | 1740 | | |
1736 | 1741 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1729 | 1729 | | |
1730 | 1730 | | |
1731 | 1731 | | |
1732 | | - | |
1733 | | - | |
| 1732 | + | |
| 1733 | + | |
| 1734 | + | |
| 1735 | + | |
| 1736 | + | |
| 1737 | + | |
| 1738 | + | |
1734 | 1739 | | |
1735 | 1740 | | |
1736 | 1741 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1729 | 1729 | | |
1730 | 1730 | | |
1731 | 1731 | | |
1732 | | - | |
1733 | | - | |
| 1732 | + | |
| 1733 | + | |
| 1734 | + | |
| 1735 | + | |
| 1736 | + | |
| 1737 | + | |
| 1738 | + | |
1734 | 1739 | | |
1735 | 1740 | | |
1736 | 1741 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1686 | 1686 | | |
1687 | 1687 | | |
1688 | 1688 | | |
1689 | | - | |
1690 | | - | |
| 1689 | + | |
| 1690 | + | |
| 1691 | + | |
| 1692 | + | |
| 1693 | + | |
| 1694 | + | |
| 1695 | + | |
1691 | 1696 | | |
1692 | 1697 | | |
1693 | 1698 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1729 | 1729 | | |
1730 | 1730 | | |
1731 | 1731 | | |
1732 | | - | |
1733 | | - | |
| 1732 | + | |
| 1733 | + | |
| 1734 | + | |
| 1735 | + | |
| 1736 | + | |
| 1737 | + | |
| 1738 | + | |
1734 | 1739 | | |
1735 | 1740 | | |
1736 | 1741 | | |
| |||
0 commit comments