Sightline Tool (previously ShadeTool)#993
Open
tariqksoliman wants to merge 982 commits into
Open
Conversation
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- Icons: sm (22px) -> md (28px), mdi-14px -> mdi-18px - Playbar row centered instead of space-between - Frame label moved to its own row beneath the timeline slider - Gap between buttons: 2px -> 4px Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- Source section expanded by default, Display and Results collapsed - Chevron icon rotates 90deg when open, smooth transition - Generate button always visible outside collapsible sections - Uses existing Collapsible design-system component Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Replaced diagonal candy-stripe pattern with a diffuse left-to-right rolling gradient (18% white, ease-in-out, 1.5s loop). Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
… left - Top card (index 0) now renders on top (highest z-index) - Accent color drop indicator line shows where card will be placed - Shademaps tab cards are now drag-reorderable (with elementOrder in store) - Drag handles standardized to far left of headers in both tabs - New reorderShadeLayers function for shademaps tab layer z-ordering Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- Add child.stdin error handler to prevent unhandled error crash - Add height to required field validation (was missing) - Return 500 JSON error on non-zero Python exit instead of empty body Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
When ShadeTool.shade() hit an error path (getbands null, API failure, ll2aerll error), it set regenerating=false but never cleared changed. The auto-generate useEffect saw changed=true + regenerating=false and immediately re-triggered shade(), creating an infinite loop of getbands requests flashing between 0% and Generate. Added lastError flag to break the cycle: - Error paths set lastError=true, stopping auto-generate retries - Changing any setting clears lastError, re-enabling auto-generate - Manual Generate click still works regardless of lastError - Pan/time changes also clear lastError for fresh attempts Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Remove the separate Sweep tab. Each shade item now has a Mode selector (Static / Composite / Playback) before the Generate button: - Static: single-time shade map (existing behavior) - Composite: time-range heatmap with color ramp, opacity, legend - Playback: animated timeline with sky dome and indicators Results section moved below the Generate button, rendering per-mode. Sweep Start Time, End Time and Step Size merged into the shared vstTime section (visible when any element uses composite or playback mode). Playback controls (play/pause/step/timeline) and global sweep options (Mode, Range) appear at the bottom, visible only when sweep data exists. New switchElementMode() toggles map layers per-element. CardLegend extracted to its own component for reuse. Auto-generate disabled for non-static modes (sweeps are expensive, require explicit click). 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>
Play/pause/step buttons, timeline slider, and frame label now appear
inside each shade item's Results section when in playback mode.
Removed the global playback controls from ShadePanel bottom.
Frame labels are per-element (vstSweepFrameLabel_{id}).
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…z/el graphs Opacity is already in the Display section so no need to repeat it in composite or playback results. Playback controls (play/pause/step, timeline slider, frame label) now appear below the sky dome and az/el graphs. Added vstSweepControlsInline class for wider inline layout. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Entity (source) Select now appears in the shade item header between the checkbox and close button. Export/download dropdown moved into the Results collapsible section. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
… handler 1. ll2aerll(): fix kernel unload path mismatch (was using wrong package_dir + '/kernels/' prefix, now matches the load path) 2. ll2aerll.py bulk mode: exit with code 1 on error so Node returns 500 instead of silently forwarding a dict as success 3. utils.js ll2aerll_bulk close handler: unconditional return after error block prevents stdout from leaking on non-zero exit Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- Link toggle icon with tooltip appears left of play controls - Linked (default): playback synced across all linked shade maps - Unlinked: independent timeline per shade item - Unlinking seeds local index from current global position - Play controls aligned to the left Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
1. Close handler now falls back to stdout (where Python prints structured error JSON) when stderr is empty 2. ll2aerll_bulk requests sent as JSON (Content-Type: application/json) instead of URL-encoded form to avoid body-parser's 1000 parameter limit when sending large time arrays Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- Playback link/unlink toggle moved to right side of play controls - Export section now: Export label + Select dropdown (145px) + download icon - Removed Dropdown import (no longer used) Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- vstTime section now shows Start Time, End Time, and Step Size (removed redundant vstSweepBody time inputs) - Each shade item's Observer section now shows observer-local start/end time inputs when an observer is selected - UTC <-> observer local time conversion via chronice API - Editing observer time on blur converts back to UTC and updates the global sweep start/end times - Added convertUTCToObserver and convertObserverToUTC to ShadeTool Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- ShadeTool.make() now initializes sweepStart/sweepEnd from TimeControl on mount and keeps them in sync via the TimeControl subscription - Removed duplicate ShadeTool_Sweep subscription from ShadePanel (single source of truth is now ShadeTool's subscription) - Observer local times update reactively since they depend on sweepStart/sweepEnd via useEffect Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Raw Python tracebacks were being sent to the client in the error message field. Now only the generic exit code is returned; stderr is still logged server-side for debugging. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Apply encodeURIComponent to target, obsRefFrame, and obsBody before passing to Python, matching the single ll2aerll endpoint's approach. Prevents directory traversal via crafted body/target values used in kernel path construction. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Add unquote() calls for target, obsRefFrame, and obsBody in bulk mode to match single mode, since the Node endpoint now applies encodeURIComponent to these fields. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- Add React-lifecycle TimeControl subscription in ShadePanel that keeps sweepStart/sweepEnd in sync with TimeUI (handles both Range and Point modes) - Remove redundant external sync from ShadeTool.make() subscription (single source of truth is now the component-level subscription) - Observer times fall back to UTC display if chronice API is unavailable Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- CardLegend shows draggable handles at bin boundaries in discrete mode - Dragging stops adjusts the width of each color bin in real-time - Heatmap re-renders with custom stop positions (evalColorWithStops, getBinForValue) - Reset icon appears when stops differ from defaults, restores even spacing - colorStops stored per-element in sweepElData Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- Color stop handles show their % position on hover/drag - Drag uses local state for instant visual preview of gradient bands - Heatmap re-render only fires on mouse release (not during drag) for better performance with large heatmaps Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- CardLegend: use ref to track drag stops, fire onColorStopsChange via setTimeout after state settles (not inside state updater) - _onPanEnd: skip auto-regeneration of static shade for elements in composite/playback mode; panning only marks sweep as stale (re-enables the Sweep button) Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Both the Earth (USGS SF Hill) and Lunar South Pole (LRO LOLA 4000m) DEMs are now tiled COGs with deflate compression and overviews. This ensures consistent behavior with the sightmap COG requirement and enables fast overview-based reads at any resolution. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…server time sync - chronice.py: add lunar LSMT support using SPICE et2lst with observer longitude; format: LDAY-NNNNNLHH:MM:SS; reverse conversion via iterative refinement - utils.js: pass optional lng param through to chronice.py - SightlineTool.js: remove TimeUI indicator on mode switch, cancel sweep, resweep start, and pan-end; pass lng from observer point for LSMT observers - SightlineElement.jsx: update global TimeControl when observer time inputs are changed (blur/Enter), fixing Mars SOL time not updating the TimeUI - Lunar ref mission config: add Moon (LSMT) observer with type=lsmt Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…key on observer time - _getObserverLng: fall back to map center when indicatorLastDragPoint is null - SightlineElement: hide DEM dropdown when no data options configured - SightlineElement: add onKeyDown Enter handler on observer time inputs Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…e config descriptions - Only use _projImageOverlay and viewport clipping when the mission uses a custom projected CRS (projection.custom=true). For standard longlat/ Mercator missions (like Mars), the DEM's projected bounds are in a different CRS than the map, causing misplaced overlays. - Restore Layer-specific DEMs config row and improve field descriptions in sightline tool config.json (lost during ShadeTool→SightlineTool rename). - Add sweepColorRamps and observer type examples to descriptionFull. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…a row, clear default name - Remove Layer-specific DEMs config row (previously asked to remove) - Restore detailed descriptions from old ShadeTool config: - Sources: documents name/value properties, dropdown usage, kernel path - Observers: documents name/value/frame/body, chronos setup path - Default Height: full description of height parameter behavior - Observer Time Placeholder: documents format string usage - Frame/Body fields: proper SPICE reference descriptions - Remove 'Sightline N' default element name (now empty) Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…instead of UTC
Root cause: chronice lmst→utc returns '2026-05-30T21:36:57.975' (no Z suffix).
The old ShadeTool correctly did: result.replace(' ', 'T') + 'Z'
The new code used a regex chain that failed when milliseconds were present
without a trailing Z, leaving the string timezone-ambiguous. new Date()
then parsed it as local time (UTC-7), adding ~7 hours.
Fix: strip milliseconds then unconditionally append Z, matching the old
ShadeTool approach. The /ZZ$/ → Z guard prevents double-Z if chronice
ever returns a Z-suffixed result in the future.
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…t, tab-switch regen, add editable time field 1. Sightmap sun direction: _compute_directions now only applies convergence rotation for azimuthal projections (stereo/gnomonic). For cylindrical projections (Equidistant Cylindrical, Mercator), grid north = geographic north so convergence = 0. Previously applied polar-stereo formula to all projected CRS, giving ~90deg rotation on Mars DEM. 2. Observer time 1-second drift: restored _lastConvertedMs pattern from old ShadeTool. Saves sub-second precision from observer->UTC conversion and re-attaches it in UTC->observer reverse conversion for exact round-trips. 3. Tab-switch regeneration: _onTimeChange now tracks _lastGeneratedTime and skips if unchanged, preventing redundant sightmap computation when TimeControl re-broadcasts the same time on tab refocus. 4. Editable time field (vstOptionTime): restored from old ShadeTool. Shows current end time in configured format (DOY, etc), editable on blur/Enter. Parses via utcTimeFormat if configured, else appends Z directly. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- CSS matches old ShadeTool exactly: full-width centered input, bold 14px, color-p0 bg, color-a1-5 text, transparent border that shows color-c on focus - Clock icon positioned absolute right (pointer-events: none) as in original - Structure uses flexbetween wrapper matching old jQuery markup - Mars reference mission utcTimeFormat changed to DOY: '%Y-%j %H:%M:%S' giving output like '2026-150 21:36:57' instead of ISO format Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…text non-selectable 1. HorizonProfile.py _grid_convergence: same fix as sightmap.py — only apply convergence for azimuthal projections (stereo/gnomonic). For cylindrical projections (Mars Equidist. Cylindrical), convergence = 0, so horizon terrain profile azimuths are now correct. 2. Visibility chart (.sightlineVisWrap): added user-select: none so dragging the timeline scrubber doesn't accidentally highlight text. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…ing/lag - HorizonProfile.py: compute per-axis pixel scales (px_scale_x, px_scale_y) so the march direction accounts for longitude compression at observer latitude. For geographic CRS at 38°N, 1° lon ≈ 0.79 × 1° lat in meters; without this the march traces wrong physical angles, distorting azimuths. Also computes correct per-step physical distance instead of using the averaged pixel_scale. - Crosshair restyled: smaller (8px circle, 5px arms), lime green with black borders (box-shadow outline). - Crosshair converted from raw DOM element to Leaflet DivIcon marker. Leaflet handles positioning in its own transform pipeline, eliminating the lag that occurred when updating CSS left/top on the move event. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…is open Small 6px lime green dot with 1px black border, always at 50%/50% of the map container (CSS-only positioning, no event tracking needed). Added on make(), removed on destroy(). Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Previously the crosshair only corrected its position on the next pan event. Now _updateCrosshairPosition() is called right after sweepCenter is stored for both static sightmap and batch/sweep completion. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
- Add SAFE_NAME_RE validation on target, obsRefFrame, obsBody to prevent
directory traversal via SPICE kernel paths (matches /ll2aerll_bulk).
- Add MAX_TIMES=200 cap on sightmap batch to prevent resource exhaustion.
- Fix E2E test: batch response is a raw JSON array, not { results: [...] }.
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Match ll2aerll_bulk pattern: handle child.on('error') and
child.stdin.on('error') to prevent hung responses if Python
fails to start. Add !res.headersSent guards on all response
paths in the close handler.
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
After open_dem decimates a large DEM, gt[5] is scaled but ds still has the original RasterYSize. Using ds.RasterYSize with the decimated gt produces a wrong mid_lat for geographic CRS pixel scale. Now accepts dem_rows directly from dem.shape. Also: encodeURIComponent the chronice lng argument to match the other CLI args (consistency with unquote() on the Python side). Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
cross(normal, north) yields West, not East. Changed to cross(north, normal) to match the batch version _sun_azel_batch. Currently unused at runtime but prevents future bugs. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Removed unused scalar functions that were superseded by vectorized equivalents: sun_azel_at_cell (replaced by _sun_azel_batch), is_nodata (replaced by _vectorized_is_nodata), geo_to_pixel (never called). Also removed the unused ds parameter from open_dem return value and _compute_bounds signature — ds was only kept alive for get_pixel_scale which no longer needs it after the dem_rows fix. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
SightlineTool.js: removed showSightlinemapLayers, showSweepLayers, refreshAllHeatmaps, _nextPow2 — all defined but never called. SightlineTool_Algorithm.js: removed the entire old client-side sightline algorithm (sightline, processUp/Down, mask, curveData, isNoData, compositeResults, calcHeight*, initializeGrids, perOctant) and their unused imports (jquery, F_, L_, G_). Only cumulativeVisibility is called externally; all other methods were from the pre-backend era and superseded by sightmap.py. SightlineTool_Graphs.js: removed _localNorthAngle, replaced by the geodesic _destinationPoint + _azimuthEndpoint approach. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Remove overflow:hidden from vstSightlineItem, vstSweepCard, and vstSweepCardsSection so absolutely-positioned color picker palettes and color ramp dropdowns are no longer clipped by their parent containers. Add border-radius to headers directly to preserve rounded corners. Bump vstColorPalette z-index from 100 to 10000 to match the ColorRampPicker popup z-index. Reorder MULTI_SOURCE_COLORS: yellow -> blue -> red -> green (swapped blue and red positions). Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
et2lst returns integer (hr, mn, sc) so one lunar second spans ~29 ET seconds. The old iterative loop converged to ±1 lunar second, giving ~30s UTC precision. Now uses binary search after the coarse loop to find the exact ET boundary where the second ticks over, narrowing to <0.5 ET seconds. Result is placed at the midpoint of the lunar second window for minimal round-trip error. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
The Collapsible panel has overflow:hidden for its open/close animation, which clips the color picker dropdown. Changed the palette to position:fixed, computed from the swatch's bounding rect on click, so it escapes all overflow containers. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Reverts position:fixed approach. Instead overrides overflow to visible on open Collapsible panels inside sightlineTool via [data-open] selector, so the color palette can extend past the panel boundary while keeping overflow:hidden during animations. Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
…-config feat: SightlineTool + backend sightmap + polar projection fixes + Numba JIT
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.
With Devin
JPL-Devin#87
JPL-Devin#88
JPL-Devin#90