| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
feat(mcp): add read-only map previews with MCP Apps (#3097) | 14 天前 | |
feat: add DashMap support for Plotly Dash (#2136) * Add DashMap component * feat: add DashMap support * style: auto-format (ruff + oxfmt) [pre-commit.ci] * Address CodeRabbit review feedback - DashMap front end: the app posts "geolibre:ready" only once per iframe load, so a project change on a mounted iframe never reached it. Track readiness in a ref and push the latest project when `project` changes after ready, mirroring the anywidget front end's ready + onProjectChange pattern. Message handlers now read the latest props through a ref (so a changed setProps is never stale) and each load carries an incrementing seq instead of a constant 1. - Add tests/dash-component-frontend.test.ts covering the ready handshake, the project-change-after-ready push (fails without the fix), the before-ready case, cross-origin/foreign-window rejection, and the click -> clickData mapping. - Install Dash in the publish-python test job and add it to the `dev` extra, so test_dashmap_requires_dash_or_has_component exercises the installed-Dash branch instead of only the ImportError fallback. Claude-Session: https://claude.ai/code/session_01J6hyqLp3Cfym5WGZjsxYTT --------- Co-authored-by: arich <arich@scwa.ca.gov> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: giswqs <giswqs@gmail.com> | 1 个月前 | |
chore(release): v3.4.0 (#3115) | 13 天前 | |
ci(fmt): add ruff + oxfmt auto-format workflow and config (#1327) * ci(fmt): add ruff + oxfmt auto-format workflow and config Adds a GitHub Actions workflow that runs ruff format/check on Python and notebooks and oxfmt on JS/TS/JSON/CSS/YAML/TOML, then pushes a style commit back to the branch. Mirrors the local pre-commit hooks so CI and local runs produce identical output. - .github/workflows/format.yml: pinned ruff==0.15.22, oxfmt@0.59.0 - ruff.toml: line-length 100, rules F/I/W/E, py310 target - .oxfmtrc.json: width 100, double quotes, lockfile/generated excludes - .pre-commit-config.yaml: local ruff + oxfmt hooks (offline-friendly) - .gitattributes: normalize all text to LF, explicit binary rules - docs/contributing.md: document the new formatting pipeline * ci(fmt): switch auto-format from GitHub Actions to pre-commit.ci pre-commit.ci authenticates via its own GitHub App, so the auto-format bot can push fixes back on fork PRs (where GITHUB_TOKEN is read-only). It only runs on PRs, so auto-formatted commits never bypass review. - Delete .github/workflows/format.yml - Add autofix/autoupdate config to .pre-commit-config.yaml; the ruff and oxfmt version pins there are now the single source of truth - Trim ignorePatterns comments in .oxfmtrc.json - Rewrite the coding-conventions section in docs/contributing.md * ci(fmt): address review feedback on ruff.toml and .gitattributes - .gitattributes: add explicit binary rules for *.icns, *.pbf, *.pmtiles (icon.icns, MapLibre glyph pbfs, osm.pbf fixtures, and mini.pmtiles are all tracked in-repo; closes the explicit-safety-net gap flagged in review) - ruff.toml: drop the misleading lines-after-imports = -1 (no-op default) and its comment; add .ruff_cache to extend-exclude so the intent survives any future change to ruff's default exclusion list - .gitignore: ignore .ruff_cache/ - .pre-commit-config.yaml: drop the redundant `files: \.(py|ipynb)$` regex (types_or already scopes the ruff hooks to python/pyi/jupyter) and fix the nbstripout hook indentation * ci(fmt): exclude generated whitebox-menu-catalog from oxfmt The file is auto-generated by scripts/gen-whitebox-menu-catalog.mjs (its header says 'do not hand-edit'), so add it to .oxfmtrc.json's ignorePatterns alongside the other generated catalogs. * ci(fmt): run ruff check --fix before ruff format Lint autofixes (import sorting via I, unused-import removal via F) can change layout; the formatter must run last so a single pre-commit pass stabilizes the file. Matches the order recommended by ruff's docs and astral-sh/ruff-pre-commit. * Update .gitattributes Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * style: auto-format (ruff + oxfmt) [pre-commit.ci] * Address Claude review feedback - docs/contributing.md: note that oxfmt also sorts package.json keys into its conventional order, so contributors expect key reordering (not just whitespace changes) in future package.json diffs. * Address Claude review feedback - .oxfmtrc.json: drop the "python/**" ignore. It was a no-op for .py/.ipynb (the hook's types_or never passes them, and *.ipynb is excluded globally) while wrongly hiding real JS/TOML under python/ from oxfmt; the generated python/src/geolibre/static stays excluded via "**/static/**". Format the newly covered python/pyproject.toml and python/src/geolibre/_frontend.js. - ruff.toml: delete the [lint.pycodestyle] max-line-length block; it equaled the top-level line-length, which is already the default, so behavior is unchanged. Reworded the E-rules comment that pointed at it. - docs/contributing.md: restore the 2-space list-continuation indent on the mixed-line-ending bullet so the inline code span isn't split oddly. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Qiusheng Wu <giswqs@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> | 3 个月前 | |
feat(python): arm Identify, toggle controls, and set projection from Python (#2603) * feat(python): arm Identify, toggle controls, and set projection from Python Add Map.set_identify, show_control/hide_control, and set_projection so a notebook can open a map with click popups armed, the Bookmarks and Search panels shown, and a flat map instead of the globe. Identify and control visibility ride a synced _ui trait the widget replays on load and after every project push, so they work before the map is displayed. Refs #2600 * Address review feedback - Record every script-set map control at module scope in ui-controls.ts and replay it from a new useScriptControlRestore hook in DesktopShell, keyed on the controller and project generations. The toolbar was the only thing re-applying a scripted show_control/hide_control, and it is unmounted in `?maponly` embeds, so a hidden control came back on a renderer swap or project load. This follows the split useTerrainRestore already uses for terrain, which exists for exactly this reason. - The same record covers a script that runs before the map controller exists: it is created asynchronously and `geolibre:ready` is emitted from a mount-only effect, so a command flushed right after ready could find no controller and be silently dropped. It is now applied once the controller is ready. - Note in TopToolbar that its listener only mirrors the checkmark, so the restore responsibility is not moved back there later. - Cover the record in tests/scripting-ui-controls.test.ts: what is recorded, last-write-wins per control, and that it starts empty. * Address review feedback - Drop a script's recorded control override when the user toggles that control from the Controls menu. Without it the replay added in the previous commit would force the scripted value back on the next renderer swap or project load, overriding an explicit user choice. Covered by three tests, which fail without the drop. - Factor the duplicated `controllerRef` getter literal in scriptingApi.ts into a shared `controllerRefFrom` helper, removing the repeated cast. - Disarm Identify before pushing the project in `remove_layer` and `clear_layers`. The two traits sync independently, so clearing afterwards left a window where the front end could replay `identify` for a layer the project push had just deleted. * Address review feedback - Drop a pinned Identify target in `load_project` when the incoming project does not carry that layer. The guard added for `remove_layer` and `clear_layers` missed the most drastic replacement of all: a stale layer id survived in `_ui` and was replayed on the next sync, which the front end rejects into a reply nobody reads, disarming Identify via a stray error rather than cleanly. "all" and None survive any project, as before. - Cover it in test_ui_controls.py: the layer is gone, the layer survives, "all" is untouched, and the existing remove_layer path. * Address review feedback - Forget every scripted control override when the user starts a New Project. That reset returns all controls to their defaults, so the overrides are spent; without this, useScriptControlRestore re-applied them to the live map on the same project-generation bump (parent effects run after the toolbar's) and left the map disagreeing with the checkmarks just reset. - Cleared in the toolbar's New Project reset rather than in the store's newProject/loadProject: the widget's project push goes through useAppStore.loadProject, so clearing there would wipe the controls a script set on every push, which is the case the app-side replay exists to serve. * Address review feedback - Guard the hand-mirrored control lists against drift: MAP_PANELS and MAP_CONTROLS in geolibre.py duplicate SCRIPTABLE_PANELS and SCRIPTABLE_MAP_CONTROLS in ui-controls.ts by hand, and nothing failed when the two diverged, so a control added on one side only would be accepted by show_control and rejected by the app (or the reverse). The test reads the TypeScript constants and compares them, and skips where the frontend source is not in the checkout. CLAUDE.md calls out silently drifting hand-mirrors as a recurring trap in this repo. * Address review feedback Document two behaviours reviewers asked about, neither of which changes: - Why setControlVisible does not gate on setBuiltInControlVisible's return value: it is not a clean success flag, since the MapLibre engine returns false when a control is already mounted, so an idempotent show_control would report failure. Separating that from a real refusal needs a visibility getter on MapEngine, which does not exist. - That the "all" Identify sentinel is matched before the layer lookup, so a layer whose id or display name is literally "all" cannot be targeted on its own. Noted on both the TypeScript helper and Map.set_identify. --------- Co-authored-by: giswqs <5016453+giswqs@users.noreply.github.com> | 1 个月前 | |
fix: proxy local raster URLs in Colab (#2108) * fix: proxy local raster URLs in Colab Route session-scoped raster files through Colab's kernel proxy so xarray layers can load remotely. Always strip outputs from every tracked notebook during pre-commit runs. * Address Claude review feedback - Support local raster rewriting through jupyter-server-proxy as well as Colab. - Rewrite sourcePath independently when it references a registered local file. - Preserve git stderr when notebook discovery fails. * Address CodeRabbit review feedback - Terminate nbstripout option parsing before passing tracked notebook paths. * fix: rebase stale Colab raster proxy URLs Colab gives separate widget views distinct proxy origins. Rebase tokenized raster URLs from an older view onto the current view before restoring layers. * fix(python): preserve raster range requests through Colab proxy * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(python): tunnel partial raster responses through Colab * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(python): render local rasters as XYZ tiles in Colab * Address review feedback - _server.py: gate the Content-Range/X-GeoLibre-Content-Range headers on a newly parsed range rather than on the presence of a Range header, so a non-bytes unit (e.g. "items=0-1") no longer emits a spec-invalid Content-Range alongside a full-file 200. - _server.py: send Access-Control-Expose-Headers for X-GeoLibre-Content-Range, without which a cross-origin fetch cannot read it and the app leaves the response at 200 instead of rebuilding the 206. - _frontend.js: require an empty port on the Colab proxy hostname, so a look-alike host on a foreign network port does not enter the rewrite path. - _frontend.js: extract the transport gate into an exported canProxyLocalFiles(base, port) and cover it in tests, so a future proxyBase/resolveBase refactor cannot silently stop the rewrite. - geolibre.py: resolve the local raster path once and hand the resolved path to both register_raster_tiles and rasterio.open — GDAL does not expand "~", so a "~/dem.tif" source silently lost its computed bounds. - kernel-proxy-range.ts: export KERNEL_CONTENT_RANGE_HEADER and cross-reference the three mirrored literals from _frontend.js and _server.py; a new test asserts both Python sources against the exported constants. - python-api.md: fix the duplicated "JupyterHub when" clause, matching the wording in docs/python.md. - tests: cover the malformed-range 200, the exposed range header, and both the populated and None bounds paths of add_cog's Colab branch. * Address Claude review feedback - add_cog: the `url:` docstring still claimed a local raster is only reachable when the browser runs on the kernel's host, which this PR made stale. Restate it the way docs/python.md now does: direct reads in local Jupyter/VS Code, kernel-rendered PNG XYZ tiles in Colab, the kernel port via jupyter-server-proxy on JupyterHub, and a hosted URL for an extension-only deployment. * Address Claude review feedback - Remove the Colab range-proxy bridge, which the PNG-tile path in 45599eff superseded and left unreachable: `register_local_file` is called only from `_resolve_raster_source`, which only `add_cog`'s non-Colab branch reaches, so no `/_geolibre_local/` URL is ever emitted under Colab and nothing ever set the `__geolibre_range_proxy` marker the bridge keys on. Deleted `kernel-proxy-range.ts` and its global `fetch` wrapper (installed in `main.tsx` for every GeoLibre user), the marker logic in `_frontend.js`, and the `__geolibre_range` / `X-GeoLibre-Content-Range` transport in `_server.py`, along with their tests. Ordinary `Range` header support — what local Jupyter, VS Code, and jupyter-server-proxy actually use — is unchanged, including the `range_matched` fix from 8af3e91c. - Document the notebook hook's blast radius in docs/contributing.md and expand the comment in .pre-commit-config.yaml: it rewrites tracked notebooks in place, so unstaged locally-executed outputs are erased by any commit, not merely excluded from it. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> | 1 个月前 | |
feat(bookmarks): save bookmarks in the project (#3006) * feat(bookmarks): save bookmarks in the project Bookmarks lived only in the browser's localStorage, so they did not travel with a .geolibre.json and the Python package and MCP server could not author them. - Project format: optional `bookmarks` and `bookmarkGroups` (folders), normalized on load (camera range checks, duplicate ids, bearing wrap, dangling folder ids dropped) and omitted when empty. - Store: `bookmarks`, `bookmarkGroups` and `setBookmarks`; saved, autosaved and shared in collaboration like other project sections. - Bookmarks panel: no longer persists to localStorage. It opens with the project's bookmarks, writes every edit (including host-dialog imports) back to the project, and rebuilds when a project load or a collaborator replaces them. Bookmarks saved by older versions move into the first project opened that has none, once. - Python `Map.add_bookmark` / `remove_bookmark` and MCP tools `add_bookmark` / `remove_bookmark` (camera from the saved view by default, folder by id or name, layers to show), plus bookmarks in `describe_project`. The Python round-trip fixture now carries bookmarks through the app's parseProject. - Docs: project format, Python, MCP, user guide, and the agent skill. Part of #2869 * Address review feedback - Trim a bookmark's groupId before matching it to the (trimmed) folder ids, so a hand-edited id with spaces is not silently un-filed (Claude review). - Legacy migration no longer deletes the old localStorage bookmarks: a marker key records the one-time copy, so bookmarks copied into the wrong project stay recoverable from browser storage (Claude review). - Python add_bookmark refuses a 501st bookmark before changing the project, matching the app's MAX_PROJECT_BOOKMARKS (500), with a drift test that pins the two together (CodeRabbit). * Address review feedback - Write the legacy-bookmark migration marker only once the project that received them is saved; a project discarded or replaced unsaved leaves it unset so the next empty project gets the copy (CodeRabbit). - Correct the user guide: the old browser copy is left in place but no longer updated, not kept as a backup (Claude). - Move the bookmarkGroups normalization above the print-layout comment so the comment stays attached to its code (Claude). | 18 天前 | |
ci(fmt): add ruff + oxfmt auto-format workflow and config (#1327) * ci(fmt): add ruff + oxfmt auto-format workflow and config Adds a GitHub Actions workflow that runs ruff format/check on Python and notebooks and oxfmt on JS/TS/JSON/CSS/YAML/TOML, then pushes a style commit back to the branch. Mirrors the local pre-commit hooks so CI and local runs produce identical output. - .github/workflows/format.yml: pinned ruff==0.15.22, oxfmt@0.59.0 - ruff.toml: line-length 100, rules F/I/W/E, py310 target - .oxfmtrc.json: width 100, double quotes, lockfile/generated excludes - .pre-commit-config.yaml: local ruff + oxfmt hooks (offline-friendly) - .gitattributes: normalize all text to LF, explicit binary rules - docs/contributing.md: document the new formatting pipeline * ci(fmt): switch auto-format from GitHub Actions to pre-commit.ci pre-commit.ci authenticates via its own GitHub App, so the auto-format bot can push fixes back on fork PRs (where GITHUB_TOKEN is read-only). It only runs on PRs, so auto-formatted commits never bypass review. - Delete .github/workflows/format.yml - Add autofix/autoupdate config to .pre-commit-config.yaml; the ruff and oxfmt version pins there are now the single source of truth - Trim ignorePatterns comments in .oxfmtrc.json - Rewrite the coding-conventions section in docs/contributing.md * ci(fmt): address review feedback on ruff.toml and .gitattributes - .gitattributes: add explicit binary rules for *.icns, *.pbf, *.pmtiles (icon.icns, MapLibre glyph pbfs, osm.pbf fixtures, and mini.pmtiles are all tracked in-repo; closes the explicit-safety-net gap flagged in review) - ruff.toml: drop the misleading lines-after-imports = -1 (no-op default) and its comment; add .ruff_cache to extend-exclude so the intent survives any future change to ruff's default exclusion list - .gitignore: ignore .ruff_cache/ - .pre-commit-config.yaml: drop the redundant `files: \.(py|ipynb)$` regex (types_or already scopes the ruff hooks to python/pyi/jupyter) and fix the nbstripout hook indentation * ci(fmt): exclude generated whitebox-menu-catalog from oxfmt The file is auto-generated by scripts/gen-whitebox-menu-catalog.mjs (its header says 'do not hand-edit'), so add it to .oxfmtrc.json's ignorePatterns alongside the other generated catalogs. * ci(fmt): run ruff check --fix before ruff format Lint autofixes (import sorting via I, unused-import removal via F) can change layout; the formatter must run last so a single pre-commit pass stabilizes the file. Matches the order recommended by ruff's docs and astral-sh/ruff-pre-commit. * Update .gitattributes Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * style: auto-format (ruff + oxfmt) [pre-commit.ci] * Address Claude review feedback - docs/contributing.md: note that oxfmt also sorts package.json keys into its conventional order, so contributors expect key reordering (not just whitespace changes) in future package.json diffs. * Address Claude review feedback - .oxfmtrc.json: drop the "python/**" ignore. It was a no-op for .py/.ipynb (the hook's types_or never passes them, and *.ipynb is excluded globally) while wrongly hiding real JS/TOML under python/ from oxfmt; the generated python/src/geolibre/static stays excluded via "**/static/**". Format the newly covered python/pyproject.toml and python/src/geolibre/_frontend.js. - ruff.toml: delete the [lint.pycodestyle] max-line-length block; it equaled the top-level line-length, which is already the default, so behavior is unchanged. Reworded the E-rules comment that pointed at it. - docs/contributing.md: restore the 2-space list-continuation indent on the mixed-line-ending bullet so the inline code span isn't split oddly. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Qiusheng Wu <giswqs@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> | 3 个月前 | |
ci(fmt): add ruff + oxfmt auto-format workflow and config (#1327) * ci(fmt): add ruff + oxfmt auto-format workflow and config Adds a GitHub Actions workflow that runs ruff format/check on Python and notebooks and oxfmt on JS/TS/JSON/CSS/YAML/TOML, then pushes a style commit back to the branch. Mirrors the local pre-commit hooks so CI and local runs produce identical output. - .github/workflows/format.yml: pinned ruff==0.15.22, oxfmt@0.59.0 - ruff.toml: line-length 100, rules F/I/W/E, py310 target - .oxfmtrc.json: width 100, double quotes, lockfile/generated excludes - .pre-commit-config.yaml: local ruff + oxfmt hooks (offline-friendly) - .gitattributes: normalize all text to LF, explicit binary rules - docs/contributing.md: document the new formatting pipeline * ci(fmt): switch auto-format from GitHub Actions to pre-commit.ci pre-commit.ci authenticates via its own GitHub App, so the auto-format bot can push fixes back on fork PRs (where GITHUB_TOKEN is read-only). It only runs on PRs, so auto-formatted commits never bypass review. - Delete .github/workflows/format.yml - Add autofix/autoupdate config to .pre-commit-config.yaml; the ruff and oxfmt version pins there are now the single source of truth - Trim ignorePatterns comments in .oxfmtrc.json - Rewrite the coding-conventions section in docs/contributing.md * ci(fmt): address review feedback on ruff.toml and .gitattributes - .gitattributes: add explicit binary rules for *.icns, *.pbf, *.pmtiles (icon.icns, MapLibre glyph pbfs, osm.pbf fixtures, and mini.pmtiles are all tracked in-repo; closes the explicit-safety-net gap flagged in review) - ruff.toml: drop the misleading lines-after-imports = -1 (no-op default) and its comment; add .ruff_cache to extend-exclude so the intent survives any future change to ruff's default exclusion list - .gitignore: ignore .ruff_cache/ - .pre-commit-config.yaml: drop the redundant `files: \.(py|ipynb)$` regex (types_or already scopes the ruff hooks to python/pyi/jupyter) and fix the nbstripout hook indentation * ci(fmt): exclude generated whitebox-menu-catalog from oxfmt The file is auto-generated by scripts/gen-whitebox-menu-catalog.mjs (its header says 'do not hand-edit'), so add it to .oxfmtrc.json's ignorePatterns alongside the other generated catalogs. * ci(fmt): run ruff check --fix before ruff format Lint autofixes (import sorting via I, unused-import removal via F) can change layout; the formatter must run last so a single pre-commit pass stabilizes the file. Matches the order recommended by ruff's docs and astral-sh/ruff-pre-commit. * Update .gitattributes Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * style: auto-format (ruff + oxfmt) [pre-commit.ci] * Address Claude review feedback - docs/contributing.md: note that oxfmt also sorts package.json keys into its conventional order, so contributors expect key reordering (not just whitespace changes) in future package.json diffs. * Address Claude review feedback - .oxfmtrc.json: drop the "python/**" ignore. It was a no-op for .py/.ipynb (the hook's types_or never passes them, and *.ipynb is excluded globally) while wrongly hiding real JS/TOML under python/ from oxfmt; the generated python/src/geolibre/static stays excluded via "**/static/**". Format the newly covered python/pyproject.toml and python/src/geolibre/_frontend.js. - ruff.toml: delete the [lint.pycodestyle] max-line-length block; it equaled the top-level line-length, which is already the default, so behavior is unchanged. Reworded the E-rules comment that pointed at it. - docs/contributing.md: restore the 2-space list-continuation indent on the mixed-line-ending bullet so the inline code span isn't split oddly. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Qiusheng Wu <giswqs@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> | 3 个月前 | |
Add Cesium drawing, capture, and plugin lifecycle support (#2302) * feat: support Cesium drawing capture and plugin lifecycle * feat: add keyless and COG-backed Cesium terrain * feat: expose Cesium renderer and mixed pane authoring APIs * Address review feedback - DesktopShell: dispatch plugin URL parameters before the Cesium early return so Cesium-capable plugins still receive them on the globe. - PrintLayoutDialog: gate the post-capture extent-box restore on request identity so a superseded capture cannot re-show it mid-capture; drop the redundant nested `if (map)`. - print-extent: keep the final Cesium extent preview after a successful draw and hand its disposer to the dialog, which now mirrors the MapLibre box lifecycle on the globe (re-show on open, hide during capture, clear on close/clear/unmount). - cesium-layer-sync: use the `tilesLoaded` flag (not the `allTilesLoaded` Event) for 3D Tiles readiness; turn the no-bounds, unsupported-WMTS and no-tile-URL early returns into load errors and report globe-supported kinds with an unusable source instead of leaving them pending forever. - extent-drawing/map-engine/cesium-engine: define MapExtent as unwrapped (west < east, east may exceed 180 across the antimeridian, MapLibre's convention); Cesium view bounds unwrap and the globe preview re-wraps. - map-controller: reuse suspendNavigation() in drawExtent so touchPitch is suspended while drawing. - plugin-manager: every scoped UI registration (map controls, toolbar menus, right panels) now requires the current activation generation and a live activation; deactivation advances the generation. Regression test for a retained state scope registering after deactivation. * Address CodeRabbit review feedback - useEmbedApi: hold the `ready` event until the engine for the current primary renderer is live, so a renderer hand-off (which bumps the readiness generation before the destination engine publishes) no longer advertises a bridge whose commands would answer "not ready". - authoring/geolibre: validate `secondaryMapViews` through one helper (a list of pane objects with a string id) so set_renderer, get_renderer and set_map_layout raise ValueError on hand-edited projects instead of KeyError or slicing errors; regression test via Map.load_project. * Address Claude review feedback - cesium-layer-sync: "2D only" layer kinds are skipped by getRenderStatus instead of reported as errors, so a mixed project no longer makes every globe capture, export, and readiness check fail. - cesium-engine: project() clamps a corner that sits behind the camera into the visible rectangle instead of throwing, so an extent drawn before a pan/tilt still crops to the canvas edge like MapLibre. - ControlsMenu: dispatch TERRAIN_SETTINGS_EVENT from @geolibre/map rather than a duplicated string literal. - isFullViewportMapCanvas lives once in @geolibre/map; the app copy in print-capture.ts is removed and its callers and test import the package. * Address CodeRabbit review feedback - authoring: secondary_panes() also rejects a present viewKind that is not maplibre or cesium (an omitted one still means maplibre), so get_renderer cannot return None or an unknown renderer and set_map_layout cannot keep one; regression test for omitted, valid and invalid values. * Address CodeRabbit review feedback - authoring: secondary_panes() rejects duplicate pane ids, since pane lookups take the first match and would silently read or update the wrong pane. - authoring: renderer values (viewKind, view_kinds entries, renderer) are type-checked before set membership, so a JSON array or object raises the documented ValueError instead of TypeError; regression cases added. * fix(ci): load isFullViewportMapCanvas without the @geolibre/map barrel The recorder and print export imported the helper from the package barrel, which pulls in MapLibre's stylesheet and fails under node's test runner (tests/map-recorder.test.ts). Expose it through a `@geolibre/map/map-capture` subpath (dev and publish exports, plus the tsdown entry) like the other node-safe modules, and import that instead. * fix(map): drop unrelated dependency changes swept into the CI fix The previous commit staged the whole package.json and picked up a cog-tiler-wasm dependency and a React peer-range bump from unrelated local work, without the matching lockfile entry. Revert those two hunks; the map-capture subpath entries stay. | 1 个月前 | |
feat(python): add_icesat2 / add_gedi helpers and an MCP tool (#3087) * feat(python): add_icesat2 / add_gedi helpers and an MCP tool Add geolibre.spaceborne_lidar, a Python port of the app's ICESat-2 / GEDI reader (spaceborne-lidar.ts) using h5py: ATL06, ATL08 and GEDI L2A, L2B, L4A, with the same product table, quality filters, fill values, beam types, UTC time, along-track distance, bbox filter and even thinning to max_points. On the five Phase 1 test granules it produces the same counts, coordinates and properties as the app. - Map.add_icesat2 / Map.add_gedi add the footprints as a GeoJSON point layer styled like the app's dialog (small points, quantile graduated colors on the product's main field) with metadata.sourceKind "spaceborne-lidar"; each rejects the other mission's granules. - MCP: add_spaceborne_lidar_layer reads a workspace granule. - New optional extra `spaceborne` (h5py, NumPy); h5py joins `dev` and the publish-python test install so the tests run in CI. - Docs (python.md, README, mcp.md) and the agent skill references. Part of #3078 (Phase 2: Python helpers). * Address review feedback - spaceborne_lidar.py: vectorize the coordinate check, the quality rule (rules now take the flag array) and the along-track haversine, so a GEDI beam of hundreds of thousands of shots is not walked in Python; output is unchanged (same counts, coordinates and distances on the real granules), GEDI reads about twice as fast. - spaceborne_lidar.py: a bare string for beams names one beam. - mcp/server.py: read the granule inside edit(), so an invalid project path fails before the expensive read. * Address review feedback - spaceborne_lidar.py: check the granule's mission right after the product is identified, before reading any beam, so add_icesat2 on a GEDI orbit (or add_gedi on ATL06) fails without reading the granule. | 14 天前 | |
ci(fmt): add ruff + oxfmt auto-format workflow and config (#1327) * ci(fmt): add ruff + oxfmt auto-format workflow and config Adds a GitHub Actions workflow that runs ruff format/check on Python and notebooks and oxfmt on JS/TS/JSON/CSS/YAML/TOML, then pushes a style commit back to the branch. Mirrors the local pre-commit hooks so CI and local runs produce identical output. - .github/workflows/format.yml: pinned ruff==0.15.22, oxfmt@0.59.0 - ruff.toml: line-length 100, rules F/I/W/E, py310 target - .oxfmtrc.json: width 100, double quotes, lockfile/generated excludes - .pre-commit-config.yaml: local ruff + oxfmt hooks (offline-friendly) - .gitattributes: normalize all text to LF, explicit binary rules - docs/contributing.md: document the new formatting pipeline * ci(fmt): switch auto-format from GitHub Actions to pre-commit.ci pre-commit.ci authenticates via its own GitHub App, so the auto-format bot can push fixes back on fork PRs (where GITHUB_TOKEN is read-only). It only runs on PRs, so auto-formatted commits never bypass review. - Delete .github/workflows/format.yml - Add autofix/autoupdate config to .pre-commit-config.yaml; the ruff and oxfmt version pins there are now the single source of truth - Trim ignorePatterns comments in .oxfmtrc.json - Rewrite the coding-conventions section in docs/contributing.md * ci(fmt): address review feedback on ruff.toml and .gitattributes - .gitattributes: add explicit binary rules for *.icns, *.pbf, *.pmtiles (icon.icns, MapLibre glyph pbfs, osm.pbf fixtures, and mini.pmtiles are all tracked in-repo; closes the explicit-safety-net gap flagged in review) - ruff.toml: drop the misleading lines-after-imports = -1 (no-op default) and its comment; add .ruff_cache to extend-exclude so the intent survives any future change to ruff's default exclusion list - .gitignore: ignore .ruff_cache/ - .pre-commit-config.yaml: drop the redundant `files: \.(py|ipynb)$` regex (types_or already scopes the ruff hooks to python/pyi/jupyter) and fix the nbstripout hook indentation * ci(fmt): exclude generated whitebox-menu-catalog from oxfmt The file is auto-generated by scripts/gen-whitebox-menu-catalog.mjs (its header says 'do not hand-edit'), so add it to .oxfmtrc.json's ignorePatterns alongside the other generated catalogs. * ci(fmt): run ruff check --fix before ruff format Lint autofixes (import sorting via I, unused-import removal via F) can change layout; the formatter must run last so a single pre-commit pass stabilizes the file. Matches the order recommended by ruff's docs and astral-sh/ruff-pre-commit. * Update .gitattributes Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * style: auto-format (ruff + oxfmt) [pre-commit.ci] * Address Claude review feedback - docs/contributing.md: note that oxfmt also sorts package.json keys into its conventional order, so contributors expect key reordering (not just whitespace changes) in future package.json diffs. * Address Claude review feedback - .oxfmtrc.json: drop the "python/**" ignore. It was a no-op for .py/.ipynb (the hook's types_or never passes them, and *.ipynb is excluded globally) while wrongly hiding real JS/TOML under python/ from oxfmt; the generated python/src/geolibre/static stays excluded via "**/static/**". Format the newly covered python/pyproject.toml and python/src/geolibre/_frontend.js. - ruff.toml: delete the [lint.pycodestyle] max-line-length block; it equaled the top-level line-length, which is already the default, so behavior is unchanged. Reworded the E-rules comment that pointed at it. - docs/contributing.md: restore the 2-space list-continuation indent on the mixed-line-ending bullet so the inline code span isn't split oddly. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Qiusheng Wu <giswqs@gmail.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> | 3 个月前 | |
fix(python): declare the widget front-end as ESM for Node (#2215) python/src/geolibre/_frontend.js is an ES module, but the nearest package.json is the repo root, which sets no "type". Node therefore classifies the file as CommonJS and only recovers through its automatic module-syntax detection — enough for a dynamic import(), but not for the static named import in tests/python-widget-frontend.test.ts, where the named bindings are resolved before that fallback can apply. The result depends on the Node patch version: the test passes on v22.23.2 (what CI runs) and fails on v22.22.2 with SyntaxError: The requested module '../python/src/geolibre/_frontend.js' does not provide an export named 'canProxyLocalFiles' even though package.json declares engines.node ">=22", so 22.22.2 is inside the stated contract. Node itself asks for this fix in the warning it prints while reparsing the file. Scope the declaration to the directory that needs it rather than the repo root, which would reclassify every other .js in the tree. Dependabot watches only "/" for npm and the workspaces globs are apps/*, packages/* and workers/*, so nothing else picks this file up. Keep it out of the distribution: the marker only matters when this repo's test suite imports the file, and nothing in the installed package runs Node. Verified by building the wheel both ways with the file tracked — 845 entries carrying geolibre/package.json without the exclude, 844 without it, matching the wheel built before this change. Refs #2213 | 1 个月前 | |
feat: headless point cloud pre-labels and labelled full-file rewrites (#2771) * feat: headless point cloud pre-labels and labelled full-file rewrites geolibre.pointcloud maps COPC node keys to file order, encodes labels like the app, pre-labels a local copy of a LiDAR layer with the app's Whitebox classifiers (whitebox-workflows) and streams a LAS/LAZ/COPC file with the saved labels and instance ids applied. MCP gains prelabel_point_cloud and write_labeled_point_cloud; Map gains the same. The sidecar gets a /pointcloud job (laspy[lazrs] installed into the conversion runtime on first use), and the annotator a "Full file with labels" export that runs it. * Address review feedback - Sidecar decoder matches the Python reader: a decoded-entry cap alongside the byte budget, a bad node skipped rather than failing the job, and instance ids above 32 bits rejected. - The legacy-to-1.4 upgrade (Python and sidecar) keeps the extra-bytes dimensions, identifiers and creation date, and rewrites the CRS as WKT. - Remove the label payload when a job cannot start. - Retry a failed job poll a few times; recheck sidecar availability at each session start. - Map.write_labeled_point_cloud checks the URL is a LiDAR layer. - Pre-labelling reads only classification codes, chunk by chunk. * Drop an unused laspy binding in prelabel_point_cloud * Address Claude review feedback - Install point cloud support under the conversion runtime's own setup lock, since it mutates the venv that lock guards. - Charge a failed label record's full cap to the budget, as the Python reader does, so malformed records cannot bypass it. * Address Claude review feedback - Keep job label payloads in their own temp folder and remove any older than an hour when a job is prepared, so a job that dies before its script runs cannot leave them behind. - Upgrade the waveform formats 4 and 5 to 9 and 10 (not 6 and 7), so their waveform fields carry over; in both the sidecar and the module. * Fix the waveform-format test fixture and wrap a docstring - The test fixture writes format 4/5 as LAS 1.3, which those formats require (1.2 only allows 0-3). - Wrap an over-long docstring line. * Address review feedback - write_labeled_point_cloud refuses to write over its input, which it would truncate mid-read (the MCP tool and sidecar already refuse). - Use a private payload folder when the shared one in the temp directory is owned by someone else or writable by others, so no other local user can swap a job's labels. * Address Claude review feedback - Refuse COPC hierarchy pages that lie outside the file and cap the hierarchy walk at the file size, in the module and the sidecar, so a crafted page size cannot force a huge read. | 24 天前 | |
feat: Encoded Polyline Encoder/Decoder Implementation (#2030) * feat: Encoded Polyline Encoder/Decoder Implementation Implemented a comprehensive Google Encoded Polyline codec supporting standard precision 5 (Google, OSRM) and precision 6 (Valhalla, Mapbox), unescaping of backslashes, batch parsing with custom delimiters, interactive preview and coordinate inspector in Add Data UI, processing toolbox tools, layer export, and full Python package support with zero external dependencies. * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix: Sequence was used in the type annotation of add_polyline but was never imported Sequence was used in the type annotation of add_polyline() at line 1203, but was never imported. * fix(polyline): resolve review findings across codec, export, and processing tools for Encoding Polyline #2029 - PolylineSource: use translated previewInfo string and reset delimiters on sample select - tauri-io: deduplicate polyline decoding and validate coordinate ranges for precision detection - vector-export / UI: thread precision option and add distinct precision 5 & 6 export menu actions - core & python: prevent unsafe conversion of literal \n, \r, \t in unescapePolyline - processing: validate polyline ASCII character range and handle MultiLineString roundtrips symmetrically - python: import Sequence from collections.abc and align delta rounding to Math.round() behavior * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(polyline): localize UI labels and harden coordinate validation in processing and Python API #2029 - LayerPanel & AttributeTable: localize Encoded Polyline export options via dynamic precision catalog key - PolylineSource: localize sample route labels and metric distance units via t() - vector-tools: add bounds validation ([-180, 180], [-90, 90]) for decoded coordinates in decodePolylineTool - tests: add null assertion on decoded geometry and test for out-of-bounds polyline input - python/polyline: validate coordinate bounds in encode_polyline, check character byte range in decode_polyline, and default unescape to False * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(polyline): enhance decoder validation, geometry export restrictions, and UI formatting #2029 - packages/core: export `decodePolylineDetailed` to indicate whether polylines were fully consumed without truncation. - packages/processing: update `decodePolylineTool` to skip features with incomplete coordinate pairs using `decodePolylineDetailed`. - apps/geolibre-desktop: - Format distance preview using locale-invariant SI units (`m` / `km`). - Sanitize `baseProperties.source` to file basename instead of full local path. - Strict line-compatibility check in `layerSupportsPolylineExport` to reject mixed-geometry layers. - python: validate cumulative lat/lon bounds `[-90, 90]` and `[-180, 180]` in `decode_polyline`. - tests: add regression tests for truncated coordinate decoding, mixed geometry exports, and Python coordinate bounds. * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(polyline): reject overlong varint shifts and harden geometry export restrictions #2029 - packages/core: guard polyline varint decoding loops against shifts exceeding 30 bits or 32-bit payload overflows, returning incomplete status immediately. - apps/geolibre-desktop: ensure layerSupportsPolylineExport strictly requires all feature geometries to be line-compatible. - tests: add regression tests for the overlong "_______?" varint chunk in decodePolyline and decodePolylineTool. - python: add tests covering coordinate bounds validation on latitude and longitude axes. * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(polyline): correct sample escaped data and harden coordinate validation #2029 - apps/geolibre-desktop: - Update `sampleEscaped` sample polyline in `PolylineSource.tsx` with a valid string containing a properly escapable backslash varint sequence. - Harden `hasValidPolylineCoordinates` in `tauri-io.ts` to reject empty coordinate lists and enforce non-empty total point counts. - Document precision 5 vs 6 auto-detection heuristics and bounds handling during dropped-file ingestion. * style: auto-format (ruff + oxfmt) [pre-commit.ci] * fix(polyline): enforce complete decoding in batch decoder and clean up imports #2029 - packages/core: require `complete === true` from `decodePolylineDetailed` in `batchDecodePolylines` before accepting features. - apps/geolibre-desktop: remove unused `Info` and `MapPin` imports from `PolylineSource.tsx`. - tests: add test verifying `batchDecodePolylines` skips lines with invalid characters or truncated varints. * style: auto-format (ruff + oxfmt) [pre-commit.ci] * Address review feedback - Fix the "Escaped backslashes (JSON string format)" sample, which failed to decode at all ("No valid polyline coordinates could be decoded"). The old value was not a real polyline: it carried one stray backslash that truncated a varint, and even ignoring that it decoded to coordinates off the planet (lat 130, lon 774). Replaced with a San Francisco -> Monterey route whose encoding genuinely contains a backslash, written doubled the way a JSON payload carries it, so the dialog's unescape step is what makes it decode. - Move SAMPLE_POLYLINES out of PolylineSource.tsx into a leaf module, lib/polyline-samples.ts, so the samples can be decoded in a test rather than only in the UI, and add tests/polyline-samples.test.ts covering every sample and the escaped one's with/without-unescape behaviour. - Document in parsePolylineFileLayers that the precision 5 -> 6 bounds check cannot resolve the ambiguous cases: the two decodes differ by exactly 10x, so in-bounds at precision 5 implies in-bounds at precision 6, and precision-6 data near the prime meridian/equator still imports 10x too large. --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: giswqs <giswqs@gmail.com> | 2 个月前 | |
feat(geolens): keep publish keys in the project state (#3144) * feat(geolens): keep publish keys in the project state The GeoLens plugin now saves the server URL, the map's publish key and a layer-id-to-dataset-key mapping with the project. Values are validated on load, so the API key typed into the panel and any other field a project file carries under the plugin id are never kept. The plugin is listed in PUBLISHABLE_PLUGIN_SETTINGS (TypeScript and Python) by sub-key rather than as a whole blob, so saving or sharing a project neither prompts to strip credentials nor publishes a key stored there later. Signed-off-by: Ian Shiland <ishiland@gmail.com> * fix(geolens): bound the saved layer keys and keep a __proto__ layer id The layer-key mapping is now built with Object.fromEntries, so a layer id of "__proto__" is kept as data instead of vanishing. Layer ids must be 1 to 256 characters and at most 500 entries are kept, so a hand-edited project can't fill the saved state. A test now compares the Python sub-key list with the TypeScript entry and the state module, so the two mirrors can't drift. Signed-off-by: Ian Shiland <ishiland@gmail.com> --------- Signed-off-by: Ian Shiland <ishiland@gmail.com> | 9 天前 | |
feat: ICESat-2 ATL03 photons, read lazily for the map view (#3091) * feat: ICESat-2 ATL03 photons, read lazily for the map view ATL03 granules hold every photon of an orbit segment (1-7 GB), so they are never loaded whole. A Web Worker opens the granule lazily and reads only what the map view needs: each beam's 20 m geolocation segments (found by a binary search on latitude, shared across the six beams), then just the photon index range inside the view. - Local files: the dialog picks a File (a file input in every build) and the worker mounts it with WORKERFS, so reads are FileReaderSync slices. - NASA granules: Earthaccess "Open" on an ATL03 granule hands the dialog the Earthdata relay URL; the worker reads it with synchronous ranged GETs (custom lazy device: size from a ranged GET, the bearer token, exact reads for whole chunks, an LRU block cache), split across up to six workers by beam, with live MB progress. Photons are merged and thinned evenly once. - Add Data → ICESat-2 / GEDI shows ground tracks with photon counts, surface type, lowest signal confidence and a photon cap; the layer carries h_ph, signal_conf, time and distance_km and is tagged for the along-track profile, which gains a "Photon heights" dot scatter. - Matches a brute-force h5py scan of every photon exactly (per beam) on two real granules (264 MB and 528 MB). Also follow-ups from #3087 / #3089 review: an integer dataset's _FillValue is read as an exact int (uint64 shot numbers), and the profile resets a beam or preset a layer edit made invalid and uses useId() for its clip path. Part of #3078 (Phase 2: ATL03 photon-level data). * Address review feedback - atl03-client: drop a helper worker that fails to start (handled, so no unhandled rejection) so the next read retries it; each worker caps its own part at maxPoints and the merge thins over what arrived, so a dense view does not clone millions of photons only to discard them. - atl03.ts: refuse a server that ignores Range for any start (the probe at 0 included) instead of reading a granule whole; keep ~32 MB of blocks a worker; accept views reported unwrapped across the antimeridian (east > 180). - atl03.worker: clear the previous file and disposer on re-open. - SpaceborneAtl03Options: measure an antimeridian-crossing view's width correctly, and keep existing layer metadata when tagging the layer. - AddSpaceborneLidarDialog: attach the file input while picking. - docs: list ATL03 among the popular datasets. - spaceborne_lidar.py: ignore a malformed integer _FillValue instead of failing the read. * Address review feedback - atl03-client: refuse a read after close() up front, and close helpers spawned during a read that close() interrupted. - AddSpaceborneLidarDialog: clear a stale error or status when the Earthaccess handoff opens an ATL03 granule. - atl03.ts: never read the segment arrays past the dataset's end when the granule's last segment is in view; correct the merge's doc. | 14 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 14 天前 | ||
| 1 个月前 | ||
| 13 天前 | ||
| 3 个月前 | ||
| 1 个月前 | ||
| 1 个月前 | ||
| 18 天前 | ||
| 3 个月前 | ||
| 3 个月前 | ||
| 1 个月前 | ||
| 14 天前 | ||
| 3 个月前 | ||
| 1 个月前 | ||
| 24 天前 | ||
| 2 个月前 | ||
| 9 天前 | ||
| 14 天前 |