Commit Graph
11 Commits
Author SHA1 Message Date
114bd7debf feat(plugins): add India GIS open geospatial and state geoportal catalog plugin (#3274)
* feat(plugins): add India GIS open geospatial and state geoportal catalog plugin

* feat(desktop): register India GIS plugin in built-in plugins list, UI profiles, and i18n locales

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* feat(plugins): provide authoritative Indian government geospatial datasets and documentation for India GIS

* style: auto-format (ruff + oxfmt) [pre-commit.ci]

* Address review feedback

- Replace the geoBoundaries raw.githubusercontent.com URLs, which serve
  Git LFS pointer text, with media.githubusercontent.com URLs that serve
  the GeoJSON with CORS.
- Load only a state's districts on each state district card: items take a
  featureFilter, and the cards keep the GADM NAME_1 values of their states.
- Name each dataset's real publisher (DataMeet, GADM, geoBoundaries) and
  drop the "authoritative/official" wording; regroup the national boundaries
  under their publishers and drop the Bhuvan entry that repeated a file.
- Credit DataMeet and link the source file on every Municipal Spatial Data
  card, as its license asks.
- Drop the Kanpur wards (Web Mercator metres) and four files that are not
  valid GeoJSON (Kochi wards and boundary, Ahmedabad zonal and ward offices).
- Correct feature counts and extents from the files themselves.
- Make check:gis-portals download every India file and check that it is a
  FeatureCollection in longitude/latitude.
- Remove the unused orgId placeholder and the searchQuery and itemFilter
  plumbing, which no India catalog used; this also restores the guard that
  never searches ArcGIS Online without an org or group scope.
- Pick a map service's first "Feature Layer" rather than its first non-group
  layer, and type subLayerIds as nullable.
- Translate the India panel through usePluginLabelSync and indiaGis.* keys.

* Address CodeRabbit review feedback

- Check every position of every retained feature for longitude/latitude
  in check:gis-portals, not only the first feature's first position.

---------

Co-authored-by: Prince Suthar <sutharprince098@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Qiusheng Wu <giswqs@gmail.com>
2026-10-28 23:57:50 -04:00
Qiusheng Wu 7f6ac2c36f fix(i18n): use sentence case for English menu labels and headings (#3243)
* fix(i18n): use sentence case for English menu labels and headings

Menu items, buttons, dialog titles and headings in en.json mixed Title
Case and sentence case. Switch the Title Case labels to sentence case,
keeping proper nouns (products, services, standards, places), acronyms,
and names of other UI surfaces ("the Layers panel") capitalized.

Processing tool names are left alone: they come from the tool registries
through the generated processing.toolMeta keys, and the matching
toolbar.*Tool menus mirror them. Projection and CRS preset names are
proper names and stay as they are.

Also:
- update the English fallbacks next to catalog keys, three hard-coded
  Add Data panel titles, and the plugin reference menu paths
- regenerate docs/user-guide/plugins.md
- update e2e selectors and unit-test expectations
- lower the same strings where another locale copied the English verbatim
  (German is left alone: it capitalizes nouns)
- document the casing and ellipsis conventions in docs/i18n.md

Fixes #3231

* fix(i18n): match the Layers header collapse label to the rail's

The left rail reads "Collapse Layers" (Collapse {{title}} with the panel
name), so the Layers header button now says the same instead of
"Collapse layers".

* Address Claude review feedback

- Match the vantor.openPanel aria-label and title fallbacks in the Vantor
  panel to the sentence-case catalog value ("Open Vantor STAC explorer").

* Address Claude review feedback

- Update English strings that quote a renamed label or menu path to the
  new sentence-case names ("Download offline area", "Add comment",
  Settings → Environment variables, Project → Export → Export layers,
  Add data → LiDAR layer, and others), including the matching code
  fallbacks and hard-coded hints.
- Keep "Open in Processing…": Processing is the toolbar menu's name, so it
  keeps its capital under the new style rule.

* Address Claude review feedback

- Match the share.role and share.expiry fallbacks in the Share dialog to
  en.json ("Access role", "Link expiry"). A sweep of every key/fallback
  pair found no other case-only mismatch.
2026-10-26 21:51:49 +00:00
Qiusheng Wu b606f13441 feat(plugins): NASA Earthaccess plugin for CMR search and Earthdata download (#3085)
* feat(plugins): add NASA Earthaccess plugin for CMR search and download

Add a Web Services plugin that searches NASA's Common Metadata
Repository for datasets and granules over the map view and a date
range, draws granule footprints as a Layers-panel entry, and downloads
granules with an Earthdata Login token. ICESat-2 (ATL06, ATL08) and GEDI
(L2A, L2B, L4A) granules open straight into Add Data -> ICESat-2 / GEDI.

The DAACs send no CORS headers and refuse a preflighted Authorization
header, so downloads need a non-browser hop:

- Desktop: a new native download_remote_file command streams the file
  to disk (no 64 MiB cap, progress over a channel, cancellable), either
  to a path picked in a native save dialog or to the app cache, read
  back once by take_cached_download. JavaScript never names a write
  path. Exposed to plugins as app.downloadRemoteFile.
- Web: a new /earthdata/download route on the tiles Worker, origin
  gated, that accepts only NASA Earthdata data hosts, sends the bearer
  token to the first hop only, follows the presigned CloudFront/S3
  redirect, and streams the file back with CORS and no-store.

Desktop users can also sign in with a user name and password (via
nativeFetch, since the EDL token endpoint has no CORS); only the
returned token is kept, in the keychain through app.credentials.

Part of #3078 (Phase 2: granule search and authenticated download).

* fix(earthaccess): Earthdata Search details link, CI checks

CMR serves granule records only as metadata formats, so Details now
opens the granule in Earthdata Search. Also add the plugin to the
generated plugin reference, list downloadRemoteFile as a desktop-only
app API member, and typecheck the Worker proxy test with the Workers
types.

* Address Claude review feedback

- earthdata.ts: catch a malformed redirect Location and answer 502 with
  CORS instead of an uncaught 500, and narrow the redirect allowlist from
  all of amazonaws.com to S3 endpoints.
- remote_download.rs: request Accept-Encoding: identity (and keep callers
  from overriding it) so the Content-Length check compares like with like.
- remote_download.rs: reject a reused request ID before the save dialog
  opens, and never let a new cache entry orphan an earlier one's file.

* feat(earthaccess): multi-file download and NASA COG map layers

- Multi-file granules (an HLS scene has 15 files) get a collapsible file
  list with a Download per file, and Download all: the desktop app asks
  for a folder once (new pick_download_folder command; JavaScript holds
  only the folder's id, never a path), the browser saves each file.
- GeoTIFFs in the list get Add, which puts them on the map as COG
  layers. The COG readers fetch with the global fetch and take no
  headers, and the DAACs send no CORS headers, so a layer reads through
  the tiles Worker relay. Its saved URL carries no token; a fetch
  wrapper installed at boot adds the Earthdata Login token to requests
  for that exact route, so reopened projects keep working.
- The Worker caches presigned redirect targets (keyed by a hash of the
  token and URL, in memory and in the edge cache, 50 minutes), cutting a
  cached range request from about 1.9 s to about 0.1 s.
- File names in the list drop the shared granule prefix (B04.tif), and
  toggling the list no longer closes it by re-rendering the card.

* fix(earthaccess): refresh Add/Added when a COG layer is removed

The panel computed a file's Added state only when it re-rendered, so
removing the layer from the Layers panel left the button disabled. The
panel now re-renders when the set of Earthdata COG layers in the store
changes, and ignores unrelated layer edits.

* Address review feedback

- earthdata.ts: restrict the content-disposition file name to
  [A-Za-z0-9._-] so an odd URL path cannot make Headers.set throw, and
  request identity encoding upstream so Content-Length matches the body.
- spaceborne-lidar-handoff.ts: isolate listener failures so one bad
  listener cannot stop the others or reach the plugin.
- maplibre-earthaccess.ts: fill MultiPolygon footprints too (CMR reports
  several polygons or boxes for some granules).
- maplibre-earthaccess.ts: freeze the search's date range so Load more
  pages the same query after the date inputs are edited.
- maplibre-earthaccess.ts: give the token link an href so keyboard users
  can reach it.
- remote_download.rs: a folder download never replaces an existing file
  (adds " (n)"), and folder ids come from a counter.

* Address Claude review feedback

- maplibre-earthaccess.ts / earthdata-relay.ts: send the Earthdata
  token only to HTTPS NASA Earthdata data hosts (isEarthdataDataUrl).
  CMR data links are chosen by each provider, so a link on another host
  gets no token on desktop, has no Add, and opens in a new tab on the web
  (the relay serves Earthdata hosts only).
- remote_download.rs: accept only Authorization, Cookie and Accept
  headers (the ones reqwest strips on a cross-host redirect, plus
  Accept), and refuse to send them over plain HTTP; the module docs now
  say exactly which headers stay on the first host.
- earthdata-fetch-auth.ts: install the fetch wrapper through an explicit
  installEarthdataFetchAuth() call in main.tsx, placed right before
  installDiagnosticsCapture(), instead of an import side effect.

* Address review feedback

- remote_download.rs: requests that carry credentials use a client whose
  redirects must stay on HTTPS, since reqwest strips credentials only
  when the host changes (a same-host https -> http hop would resend them).
- remote_download.rs: a folder download reserves its destination with
  create_new before transferring (name, then "name (n)"), streams into a
  request-specific .part file, and removes the placeholder if it fails,
  so concurrent downloads of one name never collide or replace a file.
- types.ts / remote-download.ts: GeoLibreRemoteDownloadOptions is a
  discriminated union that requires folderId for "folder", and the host
  rejects a missing id at runtime.
- maplibre-earthaccess.ts: the browser's Download all reports that the
  downloads started and mentions the multiple-downloads prompt, instead
  of claiming every file was saved.
- earthdata.ts: edge-cache hits go through rememberPresigned so the
  in-memory cap applies.

* Address review feedback

- earthdata.ts: cache the first redirect only when it is a presigned
  target, not another Earthdata host that would fail the tokenless
  replay and be evicted on every request.
- remote_download.rs: report a panicked transfer task as a failure, not
  as "cancelled".
- remote_download.rs: create the folder placeholder's cleanup guard right
  after the reservation and move it into the task, so an abort before
  the task first runs still removes the empty file.
2026-10-09 23:58:30 -04:00
Qiusheng Wu ab1d314ac7 feat(ftw): switch Fields of the World to the 2nd Edition data (#3057)
* feat(ftw): switch Fields of the World to the 2nd Edition data

FTW published a 2nd Edition (2017-2025) with a new layout: one PMTiles archive
per year holding field polygons and A5 density cells, and per-UTM-zone
GeoParquet files instead of the 1° download grid. The plugin now reads these,
filters by the new 0-100 score, and fetches only the row groups overlapping
the search area.

* Address review feedback

- Lower the GeoJSON export to 250,000 fields / 512 MiB read and the whole-file
  GeoParquet save to 256 MiB: the old caps bounded compressed bytes, not the
  decoded polygons or buffered blob, and could exhaust a tab's heap.
- Clear the result rows and footprints when a search fails, so they never
  describe a different area than the error message.
- Pin the score and density legends to LTR so their labels stay under the
  right swatches in right-to-left locales.

* Address Claude review feedback

- Skip clearing search results when the search was aborted by a newer task,
  which may still be using them.
- Word FtwTooManyFieldsError by the limit rather than the count kept so far.
2026-10-08 14:18:51 -04:00
Qiusheng Wu 63d4a583c3 feat(dynamical): draw the virtual and time-series datasets (#3050)
* feat(dynamical): draw the virtual and time-series datasets

The Dynamical panel greyed out 21 of dynamical.org's 26 datasets. Both
blockers are lifted:

- Virtual repositories point each chunk at one message in NOAA's or
  ECMWF's GRIB2 files and name the `gribberish` codec, which has no
  JavaScript build. Add a GRIB2 decoder (grid templates 3.0 and 3.30;
  packing 5.0, 5.2/5.3 and 5.42 CCSDS with a port of libaec's decoder)
  and register it as a zarrita codec, which zarr-layer reads through
  too. It matches ecCodes value for value on real GFS, GEFS, HRRR and
  AIFS messages.
- Analyses and ensembles pack hundreds to thousands of steps into each
  chunk. They now draw a region at a time: a minimum zoom is worked out
  from the chunk size and the map size so a view decodes at most
  256 MB, the map zooms in to it on add, and the layer's zoom range
  keeps a zoomed-out view (or a re-slice while zoomed out) from
  fetching anything.

Part of #3048.

* Address Claude review feedback

- grib2: a zero subdivision count falls back to microdegrees instead of
  dividing by zero, and a missing (all-ones) longitude increment leaves
  the step unset so the longitude roll is skipped.
- grib2: a truncated data section now throws instead of decoding the
  missing tail as zeros, and the encoded-value and group counts are
  checked against the grid before they size any allocation.
- grib2: orientGrib2Field defaults northUp to off, matching the codec.
- grib2-codec: document the zarrita contract the registry cast relies
  on, and test a read through zarrita end to end.
- Dynamical panel: branch on the dataset bbox explicitly and treat a
  bbox crossing 180 degrees correctly (bboxContains, bboxCenter).
- Dynamical panel: recompute regional layers' minimum zoom when the map
  resizes, since a larger map at the same zoom holds more chunks.

* Address review feedback

- grib2: validate template 5.3's differencing header (order 1 or 2, at
  least one extra octet, a data section long enough for it) instead of
  decoding NaN initial values (Claude).
- grib2: throw when the group lengths cover fewer values than the
  message declares, rather than scaling the untouched zeros (CodeRabbit).
- grib2: cap decoded grids at 16 million points, eight times the
  largest virtual grid (HRRR), so a corrupt message cannot make a
  decode allocate gigabytes (CodeRabbit).
- Dynamical panel: put the camera back if a regional add fails after
  the map jumped in to its minimum zoom (Claude).
- Dynamical panel: track regional layers even when their first minimum
  zoom is 0, so a later resize can raise it; clamp the zoom range at 0
  (CodeRabbit).
- Add an upstream contract test for zarr-layer's private minZoom and a
  docs/maintenance.md section on it (Claude).
- Drop chunkBytes' unused dataset parameter (Claude).

* test(grib2): drop the fixture files

Build the simple-packed messages inside the test with a small GRIB2
writer (grids, orientation, bitmap, truncation and count checks), and
keep the complex (5.3) and CCSDS (5.42) decoders covered by two tiny
ecCodes-encoded messages inlined as base64, checked against ecCodes'
sum, missing points and sampled values. This removes
tests/fixtures/grib2/ (13 files, including the generator).

* Address review feedback

- grib2-codec: clear the cached codec registration when the zarrita
  import fails, so a transient chunk-load error does not stick until
  reload.
- grib2: refuse a section whose length runs past the buffer with the
  descriptive "Corrupt GRIB section" error instead of truncating it or
  hitting a raw DataView RangeError; the truncation test now expects it.
- grib2: refuse a group whose length would push the decoded index past
  the declared count, rather than truncating it and accepting the layout.
- dynamical: move a regional layer's view into the dataset bbox when the
  map is already at the minimum zoom but centred outside it, keeping the
  current zoom.
- zarr: add a minZoom option that sets the renderer's own zoom gate in
  the synchronous layeradd handler, before its metadata (and first
  fetch) loads; the dynamical panel passes its regional threshold, so a
  zoom-out during the pending add cannot start a whole-globe read.
2026-10-08 12:46:36 -04:00
Qiusheng Wu 34aff3d626 feat(plugins): add a Dynamical weather data plugin under Web Services (#3023)
* feat(plugins): add a Dynamical weather data plugin under Web Services

Browse dynamical.org's STAC catalog of cloud-optimized weather forecasts
and add a variable as a Zarr raster layer read straight from its
Icechunk repository. The panel lists every dataset, enables the five
whose chunk layout suits a map (NOAA GFS forecast, NOAA HRRR 48-hour,
ECMWF AIFS Single, DWD ICON-EU, ECCC HRDPS), and explains why the rest
(time-series analyses and ensembles, GRIB-backed virtual datasets) are
unavailable.

Forecast run and lead time sliders start on the newest run and
re-slice the added layer in place. Color ranges are fixed per variable
where the physics give one and otherwise sampled from one chunk.
Projected grids are placed from the archive's CF spatial_ref: HRRR's
Lambert WKT as is, HRDPS's rotated pole rewritten as ob_tran because
proj4js ignores GDAL's GRIB pole-rotation WKT.

The STAC browser now recognizes application/x-icechunk assets and
carries a collection's datacube fields onto its collection-asset item,
so dynamical.org's collections are addable there too.

* Address review feedback

- Keep Add disabled until the chosen variable's slice axes have loaded,
  so an add that finishes mid-reload cannot re-enable it for a click
  that would add an empty selector (Claude review).
- Snapshot the axes and slider positions when Add is clicked, and write
  a sampled range back only if the same dataset and variable are still
  chosen (CodeRabbit).
- Run live re-slices one after another and rename against the current
  live record, so an older re-slice cannot land last or skip a rename
  (CodeRabbit).
- Clear the slice-axes cache on deactivate (Claude review).
- Document that Dynamical layers are not restored with a saved project,
  and "afterward" (CodeRabbit).

* Address review feedback

- Catch a failed live re-slice and show it in the panel instead of
  leaving an unhandled rejection (Claude review).
- Judge a dataset's map support by its worst variable, since the panel
  offers every variable of a dataset it lists as ready (Claude review).
- Share the re-slice queue across panel rebuilds and skip work queued
  by a disposed panel (CodeRabbit).
- Name a re-sliced layer after the variable it shows, not one chosen
  while the re-slice ran (CodeRabbit).

* Address review feedback

- Fall back to index labels only for a coordinate the repository does
  not have; a failed read now rejects and is not cached for the session
  (Claude review).
- Clear a re-slice failure from the status line once a later re-slice
  succeeds (CodeRabbit).

* Address Claude review feedback

- Use a collection's documentation link only when it is http(s), since
  it is rendered as a link; otherwise fall back to the catalog page.
- Trim the quotient before rounding a sampled range outward, so
  floating-point error cannot push a bound past its value (0.3 became
  0.29).

* fix(dynamical): keep the panel's spacing after a dataset is chosen

Un-hiding the editor set display to an empty string, which turned its
flex column into a block and dropped the gap above Forecast run,
Colormap and Add to map. Restore it as flex.
2026-10-07 14:23:44 -04:00
Qiusheng Wu 38ed979d3b docs: review the docs against the code and fix drift (#3008)
* docs: review the docs against the code and fix drift

Check every docs page against the current code and correct what no
longer matches: menu paths and labels, plugin placement (most plugins
now open in a docked panel), Cesium ion tokens being optional, desktop
OAuth sign-in for sharing, browser project save/open, project schema
fields, server and collaboration endpoints, Python and MCP APIs, and
stale paths in the source-code deep dives.

Fix the Spatial SQL tutorial's ST_Area_Spheroid query, which needs
ST_FlipCoordinates for lat/lon axis order. Document the Settings
sections that were listed but never described (Language, Appearance,
Interface, Geocoding, AI Providers), and add the USGS NLDI and ArcGIS
feature editing pages to the nav.

* docs: keep the NLDI and ArcGIS editing pages out of the nav

* Address CodeRabbit review feedback

- collaboration.md: give both snapshot-cap defaults, 10 MB for the
  relay and 1 MB in the Docker Compose setup.
- features.md, cesium-renderer.md: without an Ion token the globe still
  gets terrain from keyless Terrarium elevation (or a configured COG),
  not a bare ellipsoid (cesium-engine.ts enableWorldTerrain).

* Address CodeRabbit review feedback

- project-format.md: type blankBackgroundColor and selectedLayerId as
  string | null, matching ProjectFile in packages/core/src/types.ts.
2026-10-06 19:03:19 -04:00
Qiusheng Wu c42e054063 feat(plugins): add a Sentinel-2 Explorer under Web Services (#3001)
* feat(plugins): add a Sentinel-2 Explorer under Web Services

Ports Taylor Geospatial's s2-stac-geoparquet explorer to a GeoLibre
plugin (Plugins > Web Services > Sentinel-2 Explorer). It reads the
public catalog on Source Cooperative with no API in front of it:

- Colors the MGRS grid by monthly tile statistics over a date window
  (clearest scene, scene count, median cloud, or coverage), with max
  cloud, min coverage, and min scene filters. The grid is decoded once
  from the stats PMTiles and painted from the month slices.
- A tile click range-reads its scenes from the year's STAC-GeoParquet
  parts with hyparquet: the search sidecar or footer once per part,
  then only the column chunks of row groups whose tile range can hold
  the tile, fetched in parallel. The wide assets column is never read.
- Scenes list with thumbnails, cloud and coverage, sorted by cloud,
  coverage, or date, and filtered in memory.
- Add to map streams a scene's true color (TCI) or any single band as
  a COG layer; band stretches account for the processing-baseline
  offset.
- Supports both Collection 1 (default) and the original L2A index.

* Address review feedback

- Tag the decoded MGRS grid with the collection it was read for, and drop
  a superseded paint's grid, so a collection switch mid-load cannot keep
  the wrong grid on screen (Claude, CodeRabbit).
- Re-adding the overlays on styledata no longer throws mid basemap
  switch; the next styledata retries (Claude).
- Route the SCL stretch through bandRescale (0-19, one tab20 entry per
  class) and drop the unused SCL palette table; update the date inputs'
  minimum on a collection switch (Claude).
- Translate the band labels of the Add scenes as select (CodeRabbit).
- Check every chunk read against the cached part size and re-read a
  rewritten part's footer once before failing, and trust a part's 404 for
  five minutes only, so refreshed or newly published live tails are read
  correctly in a long session (CodeRabbit).

* feat(sentinel2-explorer): composites, indices, downloads, add/remove state

- Multi-file composites (false color IR, agriculture, SWIR) and NDVI/NDWI.
  An s2composite:// MapLibre protocol reads each band's window per map
  tile straight from the scene's COGs at the matching overview, warps it
  from UTM to Web Mercator, and paints it, so 10 m and 20 m bands
  combine at full resolution. Composites are plain XYZ store layers, and
  the protocol is registered at startup so saved projects restore them.
- Download lists a scene's COGs (TCI, every band, SCL, masks) and opens
  each in the browser, or the system browser on desktop.
- Add to map becomes Remove from map while the scene is on the map in
  the chosen display, shows Adding... while a COG loads, and follows
  the store, so removing the layer in the Layers panel resets it.
- The About text at the top of the panel is a collapsible section.

* Address Claude review feedback

- Keep a selected tile's search status (read plan or error) when the map
  repaints; the grid summary only shows with no tile selected.
- Fetch month slices at most six at a time, and memoize the aggregate by
  collection, months and metric, so filter drags read and aggregate
  nothing and a multi-year window does not fire a hundred requests.
- Reset the painted grid on deactivate so a reopened panel does not seed
  the map with the previous window's colors.
- Treat only 404 as an unpublished part or month; a 403 (blocked, rate
  limited) now surfaces as an error instead of "0 scenes".
- Clear the cached empty composite tile if its encode fails.
- Pin scene directories (and composite tile URLs) to Earth Search's two
  Sentinel-2 buckets instead of any amazonaws.com host.

* Address review feedback

- Remove the doubled comma in the Sentinel-2 Explorer features entry
  (CodeRabbit, Claude).
- Move the composite protocol registration above the Fields of the World
  comment it had orphaned in usePlugins.ts (Claude).

* Address review feedback

- Mark the composite protocol registered only after addProtocol
  succeeds, so a failed registration is retried (Claude).
- Load a result card's thumbnail only from https on Earth Search's
  Sentinel-2 buckets, the same check the COG reads use (Claude).
- Stop starting new month-slice fetches once one has failed
  (CodeRabbit).
2026-10-06 15:11:23 -04:00
Qiusheng Wu a70c2fe430 feat(plugins): add an ArcGIS Portal browser under Web Services (#2983)
* feat(plugins): add an ArcGIS Portal browser under Web Services

Signed-in ArcGIS users can now browse their own content, favorites,
groups, organization or the whole portal and add items to the map
instead of locating REST service URLs by hand. The panel shares the
Add Data sign-in session, and routes ArcGIS Online organization URLs
through www.arcgis.com because the org hosts refuse cross-origin reads.

* Address review feedback

- Send the portal token only to trusted service hosts (the portal's own
  host, or *.arcgis.com for ArcGIS Online): web map layer URLs and an
  item's registered service URL are author-controlled and could name any
  server. Untrusted ones load without credentials. Documented, with tests.
- Do not let a superseded search overwrite the panel's token after a
  newer search has started.

* Address Claude review feedback

- Treat a portal item whose search result has no service URL as
  untrusted, so the token never reaches a registered URL the panel could
  not check.
- Key the cached portal user by account as well as portal, so signing out
  in Add Data and back in as another user does not reuse the previous
  account's username, organization and favorites.
2026-10-06 01:15:18 -04:00
Qiusheng Wu a8692bdeef feat(graticule): MGRS/USNG grid overlay and grid references in Set View (#2907)
* feat(graticule): MGRS/USNG grid overlay and grid references in Set View

Add an MGRS / USNG mode to the Gridlines plugin. It draws, for the
viewport only, the grid zone boundaries (with the Norway 32V and
Svalbard 31X/33X/35X/37X exceptions) and their designations, the
100 km squares with their two-letter IDs, and 10 km then 1 km lines as
the ground resolution allows, labelled with their principal digits.
Lines are generated per zone in UTM and clipped to the zone's lng/lat
cell, and the finest tier keeps lines at least 24 px apart, so the
work stays proportional to the screen. The geometry lives in a new,
map-free mgrs-grid module; the grid type persists in plugin state and
older projects keep reading as geographic.

Share one zone rule (utmZoneNumber in mgrs-reference) between the MGRS
grid and the UTM readout, so lngLatToUtm now applies the Norway and
Svalbard exceptions and agrees with the MGRS readout (Bergen 32V,
Longyearbyen 33X).

The Set View dialog's paste box now accepts MGRS, USNG and UTM through
the place search's grid-reference parser, in addition to lat/lon.

Part of #2869

* Address review feedback

- Spell out the 10 km tier in the graticule line-width match so the
  taper (zone 2x, square 1.5x, 10 km 1x, 1 km 0.75x) does not read as a
  missed case (Claude review).
- Update the Gridlines description in scripts/plugin-reference.json, the
  source docs/user-guide/plugins.md is generated from, so the
  plugins:docs:check CI step passes.
2026-10-04 09:31:23 -04:00
Qiusheng Wu c4ac813b50 docs: generated plugin reference and deeper user guide pages (#2891)
* docs: generate the plugin reference and deepen the user guide

Generate the built-in plugin table in docs/user-guide/plugins.md from
the plugin registry (scripts/gen-plugin-reference.mjs): ids and order
from BUILT_IN_PLUGINS, names from the plugin definitions and en.json,
menu location from PluginsMenu and the Web Services/DGGS groups, and
?plugin= link names from the deep-link rules. Only descriptions are
hand-written, in scripts/plugin-reference.json. npm run plugins:docs
regenerates it; plugins:docs:check (in npm run ci) and
tests/plugin-reference-docs.test.ts fail on drift. The table now covers
all 66 registered plugins in one place.

Expand the attribute table (joins, calculated and virtual fields, field
management, attribute forms, quick filters, selection) and
troubleshooting (Diagnostics categories, notifications and Report
issue, common errors, WebGL/GPU, self-hosting CSP) pages, and add
GPS Tracking & Field Collection, Print Layout, and Model Builder pages.
Document the raster histogram stretch, palette tool entries, Simplify
Interface, and notifications.

Part of #2858

* Address review feedback

- e2e/plugin-deep-link.spec.ts: read link names from the generated
  plugin reference table, which replaced the old link-name table (E2E
  core failure).
- model-builder.md: Cancel stops before the next node; a running
  Whitebox tool finishes first.
- projects.md: list the title, footer, and info block separately.
- styling.md: viewport stretch applies to unclassified single-band and
  index rasters only.
- troubleshooting.md: a private window points at the browser profile,
  not only the cache.
2026-10-04 07:41:52 -04:00