Sightline: editable top times, cancellable sweeps, multi-DEM, native-resolution sizing#104
Conversation
…ution Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Original prompt from tariq.k.soliman
|
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
✅ Version Already UpdatedThis PR includes a manual version update to No automatic version bump needed. |
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…d effective m/px readout Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…tive resolution at native, move Resolution/Shadow Reach to Source Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…n-entered config Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…opdown - Add POST /api/sightline/visibility (visibility.py + route): single-ray, native-resolution horizon query per timestep, independent of the sweep grid. Replaces the grid-pixel-derived centerVisible for the timeline. - Add 1x-32x temporal sampling dropdown in the charts header (right of the Polygon checkbox); densifies the visibility timeline between sweep frames. - Store sweepVisSamplingRate + per-element visResults; timeline draws from the dedicated series with graceful fallback to grid centerVisible. - Docs (help + Sightline.md) and 13 new unit tests. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…r labels - Invalidate ed.visResults when a sweep completes so the visibility timeline recomputes against the new sweep instead of reusing stale data. - Rename header labels: 'Horizon' -> 'Horizon Range', 'Polygon' -> 'Horizon Polygon', 'Sampling' -> 'Visibility Sampling'. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…end visibility sampling to 256x (default 32x) - Horizon draw() retries via requestAnimationFrame until the panel has a usable size, so closing then immediately reopening the graphs renders. - Visibility Sampling options now go up to 64x/128x/256x; default is 32x. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
| "dems": [ | ||
| { | ||
| "name": "LOLA DEM (4000 m/px)", | ||
| "path": "Data/DEMs/LRO_LOLA_DEM_4000m.tif", | ||
| "resolution": 4000 | ||
| }, | ||
| { | ||
| "name": "LOLA DEM (8000 m/px)", | ||
| "path": "Data/DEMs/LRO_LOLA_DEM_4000m.tif", | ||
| "resolution": 8000 | ||
| } | ||
| ], |
There was a problem hiding this comment.
🔍 Blueprint configs reuse the same DEM file path for entries with different resolutions
In all three blueprint config files, the dems array contains two entries that point to the same physical DEM file but declare different resolution values. For example, config.reference-mission-lunar-southpole.json:249-256 lists LRO_LOLA_DEM_4000m.tif twice — once at 4000 m/px and once at 8000 m/px. Similarly config.reference-mission-mars.json:171-178 and config.reference-mission.json:4175-4182. The resolution field is a metadata hint (the DEM's native ground sample distance) used to cap the output grid and display the effective working resolution. Declaring a different resolution for the same file is misleading — the DEM's actual pixel spacing doesn't change. If these are placeholders to demonstrate the multi-DEM dropdown, a comment or distinct file paths would be clearer. If they're meant to model different downsampled views of the same terrain, the current implementation doesn't actually downsample the DEM read — it only caps the output grid dimension.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Intentional — these are placeholder entries to demonstrate the multi-DEM dropdown. The repo owner explicitly asked for each reference mission to have two DEM entries both pointing at the same file with distinct resolution values ("Both dem paths in each ref mission should just point to the same dem since its easier"). As the comment notes, resolution only caps the output grid dimension / drives the effective-resolution readout; it does not resample the DEM read, so the second entry simply models a coarser working cap over the same terrain. Leaving as-is per that request.
| const stripSeconds = (s) => | ||
| s ? s.replace(/:\d{2}Z$/, 'Z').replace(/:\d{2}\.\d+Z$/, 'Z') : '' |
There was a problem hiding this comment.
🔍 Editable time inputs strip seconds, potentially losing precision on round-trip
The stripSeconds helper in SightlinePanel.jsx:17-18 removes seconds from the displayed time (e.g., 2025-01-02T03:04:05Z → 2025-01-02T03:04Z). When the user commits the input without editing, applySweepStartTime at SightlineTool.js:2058 calls normalizeUTCTime which appends Z if missing and parses with new Date(). new Date('2025-01-02T03:04Z') is valid and sets seconds to 00. So a time originally at :30 seconds would silently become :00 if the user simply clicks into and out of the field. The onBlur handler always fires handleStartCommit/handleEndCommit, but normalizeUTCTime returns the stripped string (not the original store value), so the store gets overwritten with the seconds-truncated version. This is a minor precision loss that only matters if the user interacts with the field without intending to change it.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Intentional trade-off per the repo owner's request to make the top times "look exactly as they did before this PR, just be editable" and to drop seconds so the inputs are less wide. The minute-granularity display matches the pre-PR read-only rendering; sweep step sizes here are on the order of minutes, so committing without editing snapping :30 → :00 is an acceptable minor precision loss for this tool. If sub-minute precision on untouched blur becomes a concern, the fix would be to only re-commit when the field value actually changed — happy to add that if desired.
Model the propagation between the five editable time inputs (top Start/Step/End and per-item observer-local Start/End) through the sweep store + TimeControl + chronice, asserting every combination and order keeps all dependent inputs mutually consistent. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
🤖 Version Auto-BumpedThe version has been automatically incremented to This commit was added to your PR branch. When you merge this PR, the new version will be included. If you want a different version, update |
The horizon canvas lives in a flex child whose height keeps changing for a few frames while the graphs panel (re)opens. draw() was committing the canvas size during that transient (~18px) and never redrawing at the settled height, leaving the profile blank on close/immediate-reopen. Wait via requestAnimationFrame until the parent height is usable and stable across two consecutive frames before sizing/rendering the canvas. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
| "name": "LOLA DEM (8000 m/px)", | ||
| "path": "Data/DEMs/LRO_LOLA_DEM_4000m.tif", | ||
| "resolution": 8000 | ||
| } |
There was a problem hiding this comment.
🟡 Reference mission demo configs list two DEMs that point to the same file but claim different resolutions
The second DEM entry in each reference mission config reuses the same file path as the first but declares a different (coarser) native resolution (config.reference-mission-lunar-southpole.json:254), so selecting it produces an artificially degraded sightline map and a misleading "Native" readout in the UI.
Impact: Users choosing the second DEM option in any reference mission get unnecessarily coarse results and incorrect resolution labels.
Copy-paste error across all three reference mission configs
All three configs exhibit the same pattern — the second dems entry duplicates the first entry's path but changes only name and resolution:
blueprints/Missions/Reference-Mission-Lunar-SouthPole/config.reference-mission-lunar-southpole.json:248-256: both entries useData/DEMs/LRO_LOLA_DEM_4000m.tif, but the second claimsresolution: 8000.blueprints/Missions/Reference-Mission-Mars/config.reference-mission-mars.json:170-178: both entries useData/DEMs/missionDEM_20m_COG.tif, but the second claimsresolution: 40.blueprints/Missions/Reference-Mission/config.reference-mission.json:4174-4182: both entries useData/DEMs/USGS_13_n38w123_20250826_SFHill.tif, but the second claimsresolution: 20.
The resolution field is consumed by SightlineTool.getElementNativeResolution() (SightlineTool.js:2128-2133) and used to cap _resolutionToMaxDim() (SightlineTool.js:2345-2352). A falsely coarse resolution value caps the output grid to fewer pixels than the DEM actually supports, producing a blurrier sightline map than necessary. The UI also displays this wrong value as "Native: 8000 m/px" via SightlineElement.jsx:677.
The second entries should either point to genuinely different DEM files or, if only one file exists, be removed.
Prompt for agents
All three reference mission configs (Lunar SouthPole, Mars, and Reference-Mission) have a second DEM entry in the new `dems` array that is a copy-paste of the first entry with only the name and resolution changed — the `path` still points to the same file. This means the second option is functionally identical to the first but with a wrong resolution cap.
Either:
1. Point the second entry's `path` to a genuinely different DEM file (if one exists in the mission data), or
2. Remove the second entry entirely if only one DEM file is available for the demo.
Affected files:
- blueprints/Missions/Reference-Mission-Lunar-SouthPole/config.reference-mission-lunar-southpole.json (lines 253-256)
- blueprints/Missions/Reference-Mission-Mars/config.reference-mission-mars.json (lines 175-178)
- blueprints/Missions/Reference-Mission/config.reference-mission.json (lines 4179-4182)
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
This is intentional, not a copy-paste error. The maintainer explicitly requested each reference mission's Sightline have two DEM entries pointing at the same file with different resolution values ("Both dem paths in each ref mission should just point to the same dem since its easier") — the goal is to exercise/demo the multi-DEM selector and native-resolution readout without shipping a second large DEM file. Leaving as-is.
Switching an element's DEM at the same map position previously served the previously-selected DEM's cached horizon profile because the cache key omitted the DEM path. Add demUrl to the cache key and stored entry. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
The visibility endpoint buffers a single JSON blob and writes it at the end, so aborting the fetch (e.g. cancelling a sweep) previously left the Python process running to completion (or the spawn timeout) with its output discarded. Add a res 'close' handler that kills the child, matching the sightmap route's backstop. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- stripSeconds: anchor on the full HH:MM:SS group so a stored no-seconds time (2025-01-02T03:04Z) is not corrupted to 2025-01-02T03Z by matching the minutes. - visibility.py: emit sub-second precision in both the SPICE and ISO timestamps so high sampling rates (fine step < 1s) don't collapse consecutive samples to identical timestamps. - Add stripSeconds unit tests (incl. the no-seconds case). Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
cancelSweepElement now (1) only removes the shared TimeUI playback indicator when no other element is still sweeping, so cancelling one of several concurrent sweeps doesn't drop the others' indicator, and (2) clears the cancelled element's visResults so a late in-flight visibility fetch for the cancelled sweep isn't left displayed. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- visibility route: when computed samples exceed 32768, coarsen the step so it returns data at the cap instead of returning an error. - _fetchVisibilityFor: only re-check freshness after a *successful* fetch. On failure it previously re-ran _ensureVisibilityData, which refetched the same failing params forever (the infinite visibility calls seen when a high rate over a long range hit the sample cap). - Add backend-clamp unit tests; document the cap behavior. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Users set sweep times via the top Start/End inputs or the MMGIS timeline. Drops the per-item observer-local time inputs (and their state/effects/ handlers); the Observer dropdown stays for time conversions/visibility. Docs and time-input wiring tests updated to the three top inputs. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
The per-item time inputs were removed, freeing row space, so the dropdowns, color swatch, and text/number inputs are now 170px. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…bject URLs (revoked on rebuild/delete) Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…/cell) Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…scaled limits Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
… and ideal step size Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…weep to visibility timeline while fetching Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
… under the loading sweep Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…f clearing it Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…ht (was overwritten by stale fallback draw) Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…g about missing DEM config Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…w-edge snapping between frames Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…pping in help/docs Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
… (resolve version bump conflicts) Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Purpose
Four improvements to the Sightline Tool: make the shared top-section Start/End times directly editable, allow cancelling a running sweep, support selecting from multiple DEMs, and drive the working resolution from each DEM's real dataset resolution (meters-per-pixel from its GeoTransform) instead of screen-relative multipliers.
Proposed Changes
1. Editable top-section Start/End times
SightlinePanel.jsx: replaced the read-only Start/End<span>s with editable UTC<input>s, synced fromsweepStart/sweepEnd. On blur/Enter they commit through the same shared helpers the per-item observer-local inputs use.SightlineTool.js:normalizeUTCTime(),applySweepStartTime(),applySweepEndTime()— shared validation + global timeline/sweep-time update logic. Zoneless input is treated as UTC; invalid input reverts.2. Cancel sweeps
AbortControllerper element (_sweepAbortControllers); the sweepfetchpassessignal, and abort is handled in catch/finally.SightlineTool.cancelSweepElement(elmId)/_abortSweepRequests(elmId);cancelSweep()now aborts in-flight requests first.SightlineElement.jsx: while a sweep runs the button becomes a red Cancel (n%) button.sightmap.py: per-frame streaming loop now catchesBrokenPipeError/IOErroron stdout/stderr writes and stops cleanly when the client aborts.3. Multiple DEMs
plugin.json: newdemsobjectarray (name,path, optionalresolution) alongside the legacy singledemfield (mirrorsMeasureTool.layerDems).store.jsbuildDemsList(vars): builds the DEM list with backward-compatible fallback to the legacydem.SightlineElement.jsx: per-item DEM selector (shown only when >1 DEM configured).SightlineTool.jshelpers:getElementDem,getElementDemUrl,getElementNativeResolution,fetchElementDemInfo; the selected DEM URL is threaded into the sightmap call and (viaSightlineTool_Horizon.js) the horizonprofile call.routes/deminfo.js+scripts/deminfo.py(POST /api/sightline/deminfo): reads a DEM's GeoTransform and returns its native meters-per-pixel; registered inplugin.js; client call added incalls.js.4. Native resolution = dataset resolution
SightlineTool._resolutionToMaxDim(): computesmaxOutputDimfrom target meters-per-pixel ÷ viewport ground extent (new_getViewportGroundExtentMeters()), clamps requests finer than native to native, and falls back to the legacy relative scales when the native resolution is unknown.SightlineElement.jsx: Resolution options now show real m/px (Native, 2×, 4×, 8×) derived from the DEM's native resolution; legacy relative scales remain as fallback.Docs
public/helps/SightlineTool.mdanddocs/pages/Tools/Sightline/Sightline.md: documented editable times, cancel, multi-DEM config (dem/dems), the meters-per-pixel resolution behavior, and the newdeminfoendpoint.Issues
Testing
tests/unit/sightlineTool.spec.jswith 21 unit tests (buildDemsList backward-compat + multi-DEM,normalizeUTCTime, native pixel-scale for projected/geographic DEMs, resolution-option generation, andmaxOutputDim-from-dataset sizing incl. clamping/fallback). Full suite: 775 passed vianpm run test:unit.npx eslinton the changed JS/JSX: no new errors (only pre-existingno-undeffor the LeafletLglobal and pre-existing unused-var warnings).buildDemsListfor both the as-configured single-demshape and an injected multi-demsshape.Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/83f45496202e4e8c8119ee82aeafadee
Requested by: @tariqksoliman