Observer siting: optimal observer placement for an area#123
Merged
Conversation
Given an area (drawn ad hoc or a selected polygon feature), find a small set of observer positions inside it whose combined viewsheds maximize coverage of the area — greedy max-coverage over candidate viewsheds ((1 - 1/e) approximation of the NP-hard set-cover optimum). - candidates: local terrain maxima inside the polygon, thinned by non-maximum suppression (250 m spacing), lattice fallback for flat terrain; per-candidate viewsheds via the shared WebGPU engine - polygon rasterization by even-odd scanline (holes supported) - stops at 95 % coverage, 8 observers, or < 1 % marginal gain - radius and heights come from the last-used AoS settings - results are inserted as regular AoS documents (one undo step): viewsheds render through the standard pipeline, each observer stays individually editable; Escape cancels draw or a running computation
Candidates were local maxima sorted by elevation and capped — next to high terrain, a lower flat part of the area received no candidates at all and stayed uncovered (and dead-flat terrain flooded the peak detector, every cell being >= its neighbours). - candidates now come from block max-pooling: each block contributes its highest in-area cell, so candidates spread uniformly over the whole area while hilly blocks still yield their local summit; block size grows until the count fits the budget - sensor radius is shown in the OSD, adjustable with arrow keys while drawing the area, and persisted as the shared AoS default - result message reports coverage and radius, and suggests a larger radius when the coverage target was missed; observer cap raised to 12 - regression test: low flat plain next to mountains keeps its share of candidates
Starting the tool with a preselected polygon no longer computes right away: it arms and shows the sensor radius in the OSD — arrow keys adjust, Enter starts the computation, Escape cancels. Same radius adjustment as in the draw phase.
Tactical boundaries are LineString graphics; when a selected line forms a (nearly) closed ring — endpoints within 100 m — it is converted to a polygon and used as the siting area. Unusable selections get an OSD notice and fall back to drawing.
Observers placed by one siting run get a common random tag (e.g. OP-3f2a) so they stay recognizable — and filterable in the sidebar via #tag — as the set covering one defined area. The tag is part of the same insert batch, so one undo still removes everything, and it is reported in the OSD summary.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Observer Siting: given an area — drawn ad hoc, a selected polygon feature, or a selected (nearly) closed boundary line — find a small set of observer positions inside it whose combined viewsheds maximize coverage of that area. This answers the planning question "with how few observation posts can I overwatch this terrain, and where do they go?".
The optimization is a greedy max-coverage over candidate viewsheds ((1 − 1/e) approximation of the NP-hard set-cover optimum). Candidate positions come from block max-pooling over the elevation grid — each block contributes its highest cell, so candidates spread uniformly across the whole area (flat parts included) while hilly blocks contribute their local summit. Per-candidate viewsheds are computed by the WebGPU engine from #122, so a full run over ~100 candidates takes a few seconds with OSD progress.
Changes
observer-siting/solve.js, pure and unit-tested): even-odd scanline polygon rasterization (holes supported), block max-pooling candidate generation with budget-adaptive block size, greedy max-coverage with stop criteria (95 % coverage, 12 observers, < 1 % marginal gain)OP 1/4 - <timestamp>) — viewsheds render through the standard pipeline, each observer stays individually editable/deletable, one undo step removes the whole runOP-3f2a), reported in the OSD summary and filterable in the sidebar via#tag