Commit f35c7a2
authored
feat(netcdf): mark sampled pixels and float the spectral profile (#1713)
* feat(netcdf): mark sampled pixels and float the spectral profile
A click now records the pixel as a sampled point, drawn on the map as a
numbered dot in that point's chart color. Nothing previously showed where a
spectrum had been read from, so several lines on one chart could not be told
apart by location. This turns the profile store from a list of readings into a
list of points: the point goes in on the click so its marker lands with the
popup, and the band-axis read attaches to it by id when it resolves. A result
addressed to a point that has since been cleared, or aged off the six-point
cap, lands nowhere, so a slow read no longer needs cancelling. Points keep
their number and color when an older one falls off the cap, and Clear drops
the markers along with the chart since both derive from the same list.
The chart can also be popped out of the Style panel into a window over the
map, which is draggable and resizable -- the panel is narrow and scrolls, a
poor place to read a 285-band spectrum closely. It opens top-left because its
only resize grip is the bottom-right one, which on the right would sit against
the Style panel with nowhere to grow. The drag/resize gesture is extracted from
the Time Slider's pixel chart, which is the same kind of window over the same
container, and both now share it.
Profiles export as PNG (through the existing chart export, which resolves the
theme variables the SVG references) or CSV, one column per sampled pixel with
fill readings left empty so they read as gaps.
The x-axis labels round multiples rather than just its two ends and the
midpoint, which on a 381-2493 nm axis told you nothing about where a feature
sat. The step is chosen by which candidate's tick *count* lands nearest the
target: rounding the interval up to the next round number overshoots badly
here, turning eight labels into four.
A pixel from a 2-D grid is now kept as a point too, rather than clearing the
list. It has no band axis to profile, so it charts nothing, but it still gets
a marker and a list entry -- the readout is worth a mark on the map even when
there is no spectrum behind it.
* Address review feedback
- Give `id` its own counter that is never reset, so only `order` restarts
after a clear. Sharing one counter meant a clear recycled ids, and a band-axis
read still in flight could resolve onto an unrelated later sample that reused
its number -- silently violating the invariant that a stale read lands nowhere.
- Put the profile CSV's axis header through `displayUnits`, as the chart's own
axis label already does, so a coordinate declaring `unitless`/`1` no longer
exports as "wavelength (1)" while the chart it came from says "wavelength".
- Guard that header with `spreadsheetSafeText`: the axis name and units are free
text out of the file's own metadata, and `csvCell` escapes quotes and commas
but not a leading `=`, which a spreadsheet would execute. The point columns
are built from numbers and stay verbatim.
- Move `displayUnits` into a dependency-free `lib/cf-units` (re-exported from
`netcdf-image-symbology`, so callers are unchanged). Importing it from
`netcdf-image-symbology` reaches the plugin barrel and, through it,
`maplibre-gl-earth-engine`, which throws "window is not defined" and would have
taken the CSV builder's `node --test` run down with it.
- Repoint the stale `{@link PixelTimeSeriesControl.measureRect}` in
PixelTimeSeriesControl's geometry doc, since `measureRect` moved into
`useFloatingPanelRect`.
* Address review feedback
- Thread `mapReadyGeneration` into NetcdfSampleMarkers, the way
`useNetcdfIdentify` already takes it. The marker effect keyed only on
`samples`, so a map teardown/recreate left the markers on the discarded map
and they would not reappear until the next click changed the list.
- Give the chart's export error `role="alert"`, matching the Time Slider
chart's export error. The message lands after the click that caused it, so
without a live region a screen-reader user was never told the export failed.
- Drop `aria-hidden` from the sample list's swatch. The color is decorative but
the digit inside it is the number tying that row to its marker, and the
marker itself is hidden from assistive tech, so this list is the only place
the number can be read.
- Reuse `chart-export`'s `triggerDownload` (now exported) for the CSV instead
of a second local anchor-and-revoke helper.
- Correct the marker effect's comment: it also re-runs when a band-axis read
resolves and replaces the `samples` array, not only on a click. Left as a
full rebuild -- at MAX_PROFILE_SAMPLES markers that beats reconciling them.
- Match `FALLBACK_RECT` to the CSS default it stands in for (512x416, not
520x420), so the doc comment's claim that the two agree is true of the size
as well as the corner.
- Replace the tests' `as never` casts with a `charted()` narrowing helper, so a
change to `netcdfAxisPositions`' parameter type still fails the test file.
* Address review feedback
- Drop the `disposed` guard on a resolved band-axis read. The effect tears down
whenever the identify target changes, so switching away from a NetCDF layer
and back during a read that takes tens of seconds left that point permanently
profile-less even though the fetch completed. The store's own guard is what
makes the read safe to let land: a result for a point that has since been
cleared or aged off the cap attaches to nothing. Pending timeouts are still
cleared on teardown, so no *new* read starts after the session ends.
* Address review feedback
- Cascade the profile window's default placement 64px down. The Time Slider's
pixel chart opens at the same corner and z-index, and the two are gated
independently, so a user with a time-slider stack and a popped-out NetCDF
profile got two windows exactly on top of each other. The offset leaves that
panel's drag header exposed, and keeps this window top-left, which is what
its bottom-right resize grip needs.
- Drop the now-unused `ReactPointerEvent` type import from
PixelTimeSeriesControl, left behind when the gesture handlers moved into
`useFloatingPanelRect`.1 parent 80a80ef commit f35c7a2
16 files changed
Lines changed: 1402 additions & 475 deletions
File tree
- apps/geolibre-desktop/src
- components
- layout
- panels
- hooks
- i18n/locales
- lib
- tests
Lines changed: 7 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
126 | 126 | | |
127 | 127 | | |
128 | 128 | | |
| 129 | + | |
| 130 | + | |
129 | 131 | | |
130 | 132 | | |
131 | 133 | | |
| |||
2282 | 2284 | | |
2283 | 2285 | | |
2284 | 2286 | | |
| 2287 | + | |
| 2288 | + | |
| 2289 | + | |
| 2290 | + | |
| 2291 | + | |
2285 | 2292 | | |
2286 | 2293 | | |
2287 | 2294 | | |
| |||
Lines changed: 128 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
Lines changed: 91 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
0 commit comments