Skip to content

Sightline: editable top times, cancellable sweeps, multi-DEM, native-resolution sizing#104

Merged
tariqksoliman merged 44 commits into
developmentfrom
devin/1783471872-sightline-improvements
Jul 9, 2026
Merged

Sightline: editable top times, cancellable sweeps, multi-DEM, native-resolution sizing#104
tariqksoliman merged 44 commits into
developmentfrom
devin/1783471872-sightline-improvements

Conversation

@devin-ai-integration

Copy link
Copy Markdown

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

  • [CHANGE] SightlinePanel.jsx: replaced the read-only Start/End <span>s with editable UTC <input>s, synced from sweepStart/sweepEnd. On blur/Enter they commit through the same shared helpers the per-item observer-local inputs use.
  • [ADD] 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

  • [ADD] Client-side AbortController per element (_sweepAbortControllers); the sweep fetch passes signal, and abort is handled in catch/finally.
  • [ADD] SightlineTool.cancelSweepElement(elmId) / _abortSweepRequests(elmId); cancelSweep() now aborts in-flight requests first.
  • [CHANGE] SightlineElement.jsx: while a sweep runs the button becomes a red Cancel (n%) button.
  • [FIX] sightmap.py: per-frame streaming loop now catches BrokenPipeError/IOError on stdout/stderr writes and stops cleanly when the client aborts.

3. Multiple DEMs

  • [ADD] plugin.json: new dems objectarray (name, path, optional resolution) alongside the legacy single dem field (mirrors MeasureTool.layerDems).
  • [ADD] store.js buildDemsList(vars): builds the DEM list with backward-compatible fallback to the legacy dem.
  • [ADD] SightlineElement.jsx: per-item DEM selector (shown only when >1 DEM configured).
  • [ADD] SightlineTool.js helpers: getElementDem, getElementDemUrl, getElementNativeResolution, fetchElementDemInfo; the selected DEM URL is threaded into the sightmap call and (via SightlineTool_Horizon.js) the horizonprofile call.
  • [ADD] Backend routes/deminfo.js + scripts/deminfo.py (POST /api/sightline/deminfo): reads a DEM's GeoTransform and returns its native meters-per-pixel; registered in plugin.js; client call added in calls.js.

4. Native resolution = dataset resolution

  • [CHANGE] SightlineTool._resolutionToMaxDim(): computes maxOutputDim from 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.
  • [CHANGE] 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

  • [CHANGE] public/helps/SightlineTool.md and docs/pages/Tools/Sightline/Sightline.md: documented editable times, cancel, multi-DEM config (dem/dems), the meters-per-pixel resolution behavior, and the new deminfo endpoint.

Issues

  • N/A

Testing

  • Extended tests/unit/sightlineTool.spec.js with 21 unit tests (buildDemsList backward-compat + multi-DEM, normalizeUTCTime, native pixel-scale for projected/geographic DEMs, resolution-option generation, and maxOutputDim-from-dataset sizing incl. clamping/fallback). Full suite: 775 passed via npm run test:unit.
  • npx eslint on the changed JS/JSX: no new errors (only pre-existing no-undef for the Leaflet L global and pre-existing unused-var warnings).
  • Verified all three reference mission configs (Mars, Lunar-SouthPole, Reference-Mission) load through buildDemsList for both the as-configured single-dem shape and an injected multi-dems shape.

Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/83f45496202e4e8c8119ee82aeafadee
Requested by: @tariqksoliman

…ution

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
@tariqksoliman tariqksoliman self-assigned this Jul 8, 2026
@devin-ai-integration

Copy link
Copy Markdown
Author
Original prompt from tariq.k.soliman

Work in the JPL-Devin/MMGIS repo (branch development) on the Sightline Tool. The relevant source files are NOT in the Ask index, so first locate them on disk. Start by finding the plugin implementation (registered as "js": "SightlineTool" in the mission configs) and its algorithm module SightlineTool_Algorithm — search under src/essence/Tools/ (and check tests/unit/sightlineTool.spec.js which inlines algorithm logic and references the real module path). Also locate the backend route for /api/sightline/sightmap (search server routes for sightmap/sightline; src/pre/calls.js has the client call definitions). Read public/helps/SightlineTool.md and docs/pages/Tools/Sightline/Sightline.md for the current documented behavior.

Implement these four improvements:

  1. Edit start/end time in the top section: Currently the shared Time section at the top shows Start Time, End Time, and Step Size (connected to the MMGIS timeline), but editable Start/End inputs only appear inside a per-sightline-item once an observer is selected (observer-local, with UTC↔local round-trip conversion on blur). Make the top-section Start Time and End Time directly editable, reusing the same validation/conversion and global sweep-time update logic that the per-item observer-local inputs already use.

  2. Cancel sweeps: A sweep runs the time-range analysis across all timesteps via the batch params (startTime, endTime, stepSeconds) with per-frame progress streaming. There is currently no cancel mechanism. Add client-side cancellation via an AbortController (or equivalent) so the Sweep button toggles to a Cancel state while running and aborts the in-flight request(s); ensure the backend per-frame streaming loop stops cleanly when the request is aborted.

  3. Support multiple DEMs: The config exposes a single dem string and the sightmap dem API param is a single path. Extend the config schema to accept a list of DEMs (each with name + path, and ideally native resolution), sur... (1357 chars truncated...)

@devin-ai-integration

Copy link
Copy Markdown
Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown

✅ Version Already Updated

This PR includes a manual version update to 5.2.18-20260709

No automatic version bump needed.

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 12 commits July 8, 2026 16:38
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>
@tariqksoliman tariqksoliman marked this pull request as ready for review July 9, 2026 00:04

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

Open in Devin Review

Comment on lines +246 to +257
"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
}
],

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 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.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +17 to +18
const stripSeconds = (s) =>
s ? s.replace(/:\d{2}Z$/, 'Z').replace(/:\d{2}\.\d+Z$/, 'Z') : ''

@devin-ai-integration devin-ai-integration Bot Jul 9, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 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:05Z2025-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.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

devin-ai-integration Bot and others added 2 commits July 9, 2026 00:40
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>
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

🤖 Version Auto-Bumped

The version has been automatically incremented to 5.2.18-20260709

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 package.json manually and push to this PR.

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>

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 1 new potential issue.

Open in Devin Review

Comment on lines +253 to +256
"name": "LOLA DEM (8000 m/px)",
"path": "Data/DEMs/LRO_LOLA_DEM_4000m.tif",
"resolution": 8000
}

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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 use Data/DEMs/LRO_LOLA_DEM_4000m.tif, but the second claims resolution: 8000.
  • blueprints/Missions/Reference-Mission-Mars/config.reference-mission-mars.json:170-178: both entries use Data/DEMs/missionDEM_20m_COG.tif, but the second claims resolution: 40.
  • blueprints/Missions/Reference-Mission/config.reference-mission.json:4174-4182: both entries use Data/DEMs/USGS_13_n38w123_20250826_SFHill.tif, but the second claims resolution: 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)
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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>
devin-ai-integration[bot]

This comment was marked as resolved.

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>
devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

- 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>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 16 commits July 9, 2026 02:02
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>
devin-ai-integration[bot]

This comment was marked as resolved.

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration Bot and others added 5 commits July 9, 2026 18:39
…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>
@tariqksoliman tariqksoliman merged commit c469eba into development Jul 9, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant