commit 7df9ebf22c9af4a05c0e98e13ce8dc6fb1f77595 Author: wehub-resource-sync Date: Mon Jul 13 12:33:00 2026 +0800 chore: import upstream snapshot with attribution diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..0fb9fa5 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,20 @@ +.git +.github +.vscode +.idea +node_modules +**/node_modules +apps/geolibre-desktop/dist +apps/geolibre-desktop/src-tauri +backend/geolibre_server/.venv +backend/**/__pycache__ +workers +docs +sample-data +coverage +dist +build +*.log +.DS_Store +.env +.env.* diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..2feec2e --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +github: giswqs +buy_me_a_coffee: giswqs diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..e146858 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,76 @@ +name: Bug report +description: Report unexpected behavior or a possible flaw in GeoLibre. +title: "[Bug]: " +labels: ["unconfirmed possible issue"] +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to report a problem. Please fill out the sections below so we can reproduce and confirm the issue. + + Reports start as **unconfirmed possible issue**. Once a maintainer reproduces the problem, it is promoted to **bug**. + - type: checkboxes + id: checks + attributes: + label: Before you start + options: + - label: I searched the [existing issues](https://github.com/opengeos/GeoLibre/issues) and did not find a duplicate. + required: true + - label: This is a bug report, not a usage question (questions belong in [Discussions](https://github.com/opengeos/GeoLibre/discussions)). + required: true + - type: textarea + id: what-happened + attributes: + label: What happened? + description: A clear and concise description of the unexpected behavior. + placeholder: When I do X, Y happens instead of Z. + validations: + required: true + - type: textarea + id: reproduce + attributes: + label: Steps to reproduce + description: Tell us how to reproduce the behavior, step by step. + placeholder: | + 1. Open '...' + 2. Add data '...' + 3. Click on '...' + 4. See error + validations: + required: true + - type: textarea + id: expected + attributes: + label: Expected behavior + description: What did you expect to happen instead? + validations: + required: true + - type: input + id: app + attributes: + label: How are you running GeoLibre? + description: Web app, desktop app, or Jupyter, plus the version if you know it. + placeholder: Desktop app v0.x.y / Web (https://geolibre.app) / Jupyter + validations: + required: true + - type: input + id: os + attributes: + label: Operating system and browser + description: e.g. Windows 11 + Chrome 126, macOS 14 + Safari, Ubuntu 24.04. + validations: + required: false + - type: textarea + id: data + attributes: + label: Sample data + description: If the bug is tied to a specific dataset, share a small sample or a link so we can reproduce it. + validations: + required: false + - type: textarea + id: screenshots + attributes: + label: Screenshots or logs + description: Add screenshots, screen recordings, or console output if they help explain the problem. + validations: + required: false diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..9da5919 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Question or usage help + url: https://github.com/opengeos/GeoLibre/discussions/new?category=q-a + about: Have a "how do I do X?" question? Please ask in Discussions (Q&A) to keep the issue tracker focused on bugs and feature requests. + - name: Ideas and general discussion + url: https://github.com/opengeos/GeoLibre/discussions + about: Share early-stage ideas, show your work, or chat with the community in Discussions. + - name: Documentation + url: https://geolibre.app + about: Browse the docs and guides before opening an issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..8b47afd --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,52 @@ +name: Feature request +description: Suggest a new feature, enhancement, or UX improvement for GeoLibre. +title: "[Feature]: " +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + Thanks for helping make GeoLibre better. Please describe what you would like and why it matters. + + This form labels the issue **enhancement**. The scope you pick below is informational: a maintainer will read your selection and add the matching **enhancement (small)**, **enhancement (medium)**, or **wishlist** label during triage. + - type: checkboxes + id: checks + attributes: + label: Before you start + options: + - label: I searched the [existing issues](https://github.com/opengeos/GeoLibre/issues) and did not find a duplicate request. + required: true + - type: textarea + id: problem + attributes: + label: What problem does this solve? + description: Describe the workflow, pain point, or limitation that motivates this request. + placeholder: When working with ..., it is hard to ... because ... + validations: + required: true + - type: textarea + id: proposal + attributes: + label: Proposed solution + description: Describe what you would like to see. Mockups or examples are welcome. + validations: + required: true + - type: dropdown + id: scope + attributes: + label: Estimated scope + description: Your best guess at the size. A maintainer will confirm and apply the matching label. + options: + - "Small: a minor tweak, small UI change, or text adjustment" + - "Medium: a broader feature that is feasible in a reasonable timeframe" + - "Wishlist: a larger or longer-term idea" + - "Not sure" + validations: + required: true + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Any workarounds you currently use, or other approaches you thought about. + validations: + required: false diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1c2c33a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,92 @@ +# Dependabot keeps dependencies and their security advisories current across the +# monorepo's four ecosystems. Updates are grouped so a fast-moving repo gets a +# few consolidated PRs per week rather than a flood of one-per-dependency PRs. +version: 2 +updates: + # JavaScript — one npm workspaces lockfile at the root wires up every JS + # workspace, so a single npm entry covers apps/*, packages/*, and workers/*. + - package-ecosystem: npm + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 10 + groups: + # Security fixes land on their own, immediately actionable PRs. + npm-security: + applies-to: security-updates + patterns: + - "*" + # Routine minor/patch bumps are batched into one PR to cut review noise. + npm-minor-patch: + applies-to: version-updates + update-types: + - minor + - patch + + # Python — the FastAPI sidecar. + - package-ecosystem: pip + directory: "/backend/geolibre_server" + schedule: + interval: weekly + open-pull-requests-limit: 5 + groups: + backend-security: + applies-to: security-updates + patterns: + - "*" + backend-minor-patch: + applies-to: version-updates + update-types: + - minor + - patch + + # Python — the `geolibre` Jupyter anywidget package. + - package-ecosystem: pip + directory: "/python" + schedule: + interval: weekly + open-pull-requests-limit: 5 + groups: + widget-security: + applies-to: security-updates + patterns: + - "*" + widget-minor-patch: + applies-to: version-updates + update-types: + - minor + - patch + + # Rust — the Tauri desktop crate. + - package-ecosystem: cargo + directory: "/apps/geolibre-desktop/src-tauri" + schedule: + interval: weekly + open-pull-requests-limit: 5 + groups: + cargo-security: + applies-to: security-updates + patterns: + - "*" + cargo-minor-patch: + applies-to: version-updates + update-types: + - minor + - patch + + # GitHub Actions used by the workflows in .github/workflows/. + - package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 5 + groups: + actions-security: + applies-to: security-updates + patterns: + - "*" + actions-minor-patch: + applies-to: version-updates + update-types: + - minor + - patch diff --git a/.github/labels.yml b/.github/labels.yml new file mode 100644 index 0000000..1b778db --- /dev/null +++ b/.github/labels.yml @@ -0,0 +1,47 @@ +# Source of truth for this repository's issue/PR labels. +# Synced to GitHub by .github/workflows/labels.yml on push to main. +# The sync is additive (delete-other-labels is off), so labels not listed +# here are left untouched. + +- name: bug + color: "d73a4a" + description: Something isn't working +- name: unconfirmed possible issue + color: "fef2c0" + description: Reported behavior not yet reproduced or confirmed as a bug +- name: enhancement + color: "a2eeef" + description: New feature or request +- name: enhancement (small) + color: "c5f0f5" + description: Minor feature, small UI tweak, or text adjustment +- name: enhancement (medium) + color: "76d3dd" + description: Broader feature, feasible within a reasonable timeframe +- name: wishlist + color: "4a0aca" + description: Feature requests that require significant efforts +- name: question + color: "d876e3" + description: Further information is requested +- name: waiting on user feedback + color: "fbca04" + description: Progress depends on the reporter (e.g. sample data or verification) +- name: documentation + color: "0075ca" + description: Improvements or additions to documentation +- name: duplicate + color: "cfd3d7" + description: This issue or pull request already exists +- name: good first issue + color: "7057ff" + description: Good for newcomers +- name: help wanted + color: "008672" + description: Extra attention is needed +- name: invalid + color: "e4e669" + description: This doesn't seem right +- name: wontfix + color: "ffffff" + description: This will not be worked on diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml new file mode 100644 index 0000000..2a0fdb4 --- /dev/null +++ b/.github/workflows/android.yml @@ -0,0 +1,201 @@ +name: Android + +# Android builds run on published releases (matching release.yml), not on every +# PR — the Rust cross-compile + Gradle build is slow and rarely needs per-PR +# coverage. Use the manual "Run workflow" button (workflow_dispatch) to build a +# signed APK on demand from any branch. +on: + release: + types: [published] + workflow_dispatch: + +# Least privilege at the workflow level; the build job below elevates to +# contents: write only where it needs to attach release assets. +permissions: + contents: read + +concurrency: + group: android-${{ github.ref }} + cancel-in-progress: true + +env: + # Keep these in sync with docs/android.md and the local setup. + ANDROID_PLATFORM: "platforms;android-34" + ANDROID_BUILD_TOOLS: "build-tools;34.0.0" + # NDK r27 LTS — Tauri v2's supported line. Bump deliberately. + ANDROID_NDK_VERSION: "27.3.13750724" + +jobs: + build: + name: Build Android APK (release) + runs-on: ubuntu-22.04 + # Elevated here (not workflow-level) so only this job can write release + # assets; workflow_dispatch runs never attach but the grant is harmless. + permissions: + contents: write + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: "22" + cache: npm + cache-dependency-path: package-lock.json + + - name: Set up JDK + uses: actions/setup-java@v4 + with: + distribution: temurin + java-version: "21" + + - name: Set up Android SDK + # Third-party action pinned to a full commit SHA (v3 tag head) per the + # repo's policy for non-official actions; a tag could be re-pointed. + uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3 + + - name: Install Android NDK, platform, and build-tools + run: | + sdkmanager --install \ + "platform-tools" \ + "$ANDROID_PLATFORM" \ + "$ANDROID_BUILD_TOOLS" \ + "ndk;$ANDROID_NDK_VERSION" + echo "NDK_HOME=$ANDROID_SDK_ROOT/ndk/$ANDROID_NDK_VERSION" >> "$GITHUB_ENV" + + - name: Install Rust stable with Android targets + uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable + with: + toolchain: stable + targets: >- + aarch64-linux-android, + armv7-linux-androideabi, + i686-linux-android, + x86_64-linux-android + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 + with: + workspaces: apps/geolibre-desktop/src-tauri -> target + + - name: Install frontend dependencies + run: npm ci + + - name: Generate Android project + # gen/android is not committed; regenerate it on the clean runner. The + # Tauri CLI is resolved through the geolibre-desktop workspace. + working-directory: apps/geolibre-desktop + run: npx tauri android init + + - name: Apply GeoLibre launcher icons + # `tauri android init` writes default Tauri icons; overwrite the generated + # mipmaps with the GeoLibre launcher icons checked in under src-tauri/icons. + working-directory: apps/geolibre-desktop + run: cp -r src-tauri/icons/android/. src-tauri/gen/android/app/src/main/res/ + + - name: Build release APKs (per ABI) + # Release (not --debug): the size-optimized + stripped Cargo profile keeps + # each .so small. --split-per-abi emits one ~40 MB APK per architecture + # instead of a single ~150 MB universal APK bundling all four ABIs. + # Release APKs are unsigned by default; the next step signs them. + working-directory: apps/geolibre-desktop + run: npx tauri android build --apk --split-per-abi + env: + VITE_GEE_OAUTH_CLIENT_ID: ${{ secrets.VITE_GEE_OAUTH_CLIENT_ID }} + + - name: Sign APKs + id: sign + # With release-keystore secrets set, the APKs are signed for distribution. + # Without them, they're signed with a throwaway debug keystore so the CI + # artifacts are still installable for testing (do NOT publish those). + # Emits signed=release|debug so the release-upload step can refuse to + # attach debug-signed APKs to a public GitHub Release. + env: + KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }} + KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }} + run: | + set -euo pipefail + build_tools="$(ls -d "$ANDROID_HOME"/build-tools/* | sort -V | tail -1)" + store_pass_file="$RUNNER_TEMP/ks.pass" + key_pass_file="$RUNNER_TEMP/key.pass" + if [ -n "${KEYSTORE_BASE64:-}" ]; then + # Fail fast if the keystore secret is set but its companions are not, + # instead of a cryptic apksigner error later. + if [ -z "${KEYSTORE_PASSWORD:-}" ] || [ -z "${KEY_ALIAS:-}" ]; then + echo "::error::ANDROID_KEYSTORE_PASSWORD and ANDROID_KEY_ALIAS must be set when ANDROID_KEYSTORE_BASE64 is provided" + exit 1 + fi + echo "Signing with the release keystore from secrets." + echo "$KEYSTORE_BASE64" | base64 -d > "$RUNNER_TEMP/release.jks" + keystore="$RUNNER_TEMP/release.jks" + printf '%s' "$KEYSTORE_PASSWORD" > "$store_pass_file" + printf '%s' "${KEY_PASSWORD:-$KEYSTORE_PASSWORD}" > "$key_pass_file" + alias="$KEY_ALIAS" + sign_mode=release + else + echo "::warning::No ANDROID_KEYSTORE_BASE64 secret set — signing with a throwaway debug keystore. Installable for testing only, NOT for distribution." + sign_mode=debug + keystore="$RUNNER_TEMP/debug.jks" + "$JAVA_HOME/bin/keytool" -genkeypair -v -keystore "$keystore" \ + -storepass android -keypass android -alias androiddebugkey \ + -keyalg RSA -keysize 2048 -validity 10000 \ + -dname "CN=Android Debug,O=Android,C=US" + printf '%s' android > "$store_pass_file" + printf '%s' android > "$key_pass_file" + alias=androiddebugkey + fi + out="$RUNNER_TEMP/apks"; mkdir -p "$out" + found=0 + while IFS= read -r unsigned; do + found=1 + # e.g. app-arm64-v8a-release-unsigned.apk -> geolibre-arm64-v8a.apk + abi="$(basename "$unsigned" | sed -E 's/^app-(.*)-release-unsigned\.apk$/\1/')" + aligned="$RUNNER_TEMP/aligned-$abi.apk" + signed="$out/geolibre-android-$abi.apk" + "$build_tools/zipalign" -p -f 4 "$unsigned" "$aligned" + # Pass passwords via files (pass:file:) so they never appear in the + # process argument list / CI logs. + "$build_tools/apksigner" sign --ks "$keystore" \ + --ks-pass "file:$store_pass_file" --key-pass "file:$key_pass_file" \ + --ks-key-alias "$alias" --out "$signed" "$aligned" + "$build_tools/apksigner" verify "$signed" + echo "signed $signed ($(du -h "$signed" | cut -f1))" + done < <(find apps/geolibre-desktop/src-tauri/gen/android \ + -name '*release-unsigned.apk') + rm -f "$store_pass_file" "$key_pass_file" + if [ "$found" -eq 0 ]; then + echo "::error::No release-unsigned APKs found"; exit 1 + fi + # Emit the outcome only after every APK has signed and verified, so a + # downstream always() step can never read signed=release on a failure. + echo "signed=$sign_mode" >> "$GITHUB_OUTPUT" + + - name: Upload signed APKs + uses: actions/upload-artifact@v4 + with: + name: geolibre-android-release-apks + path: ${{ runner.temp }}/apks/*.apk + if-no-files-found: error + retention-days: 14 + + - name: Attach APKs to GitHub Release + # Only on a published release, and only when the APKs were signed with the + # real release keystore — never publish debug-signed builds as official + # downloads. workflow_dispatch runs still get the CI artifact above. + if: github.event_name == 'release' && steps.sign.outputs.signed == 'release' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.event.release.tag_name }} + run: gh release upload "$TAG" "$RUNNER_TEMP"/apks/*.apk --clobber + + - name: Note skipped release upload + # Surface why a release run did not attach APKs (missing keystore secrets), + # so it does not look like a silent failure. + if: github.event_name == 'release' && steps.sign.outputs.signed != 'release' + run: | + echo "::warning::APKs were debug-signed (no ANDROID_KEYSTORE_BASE64 secret) and were NOT attached to the release. They are available as the CI artifact for testing only." diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..a77be72 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,203 @@ +name: CI + +on: + pull_request: + branches: [main] + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + audit: + name: Dependency audit + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: "22" + + # Reads package-lock.json and queries the registry — no install needed, so + # this stays fast. Blocking at `high` catches high/critical advisories + # without reddening CI for the moderate/low noise Dependabot handles via PR. + - name: Audit npm dependencies (high and critical) + run: npm audit --audit-level=high + + e2e: + name: E2E smoke (Playwright) + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: "22" + cache: npm + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Resolve Playwright version + id: pw + run: echo "version=$(node -p "require('@playwright/test/package.json').version")" >> "$GITHUB_OUTPUT" + + - name: Cache Playwright browsers + id: pw-cache + uses: actions/cache@v6 + with: + path: ~/.cache/ms-playwright + key: playwright-${{ runner.os }}-${{ steps.pw.outputs.version }} + + - name: Install Playwright Chromium (cache miss) + if: steps.pw-cache.outputs.cache-hit != 'true' + run: npx playwright install --with-deps chromium + + - name: Install Playwright system deps (cache hit) + if: steps.pw-cache.outputs.cache-hit == 'true' + run: npx playwright install-deps chromium + + - name: Run E2E smoke tests + run: npm run test:e2e + env: + VITE_GEE_OAUTH_CLIENT_ID: ${{ secrets.VITE_GEE_OAUTH_CLIENT_ID }} + + - name: Upload Playwright report + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: playwright-report + path: | + playwright-report/ + test-results/ + retention-days: 7 + + citation: + name: Validate CITATION.cff + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install cffconvert + run: python -m pip install cffconvert + + - name: Validate CITATION.cff against the CFF schema + run: cffconvert --validate -i CITATION.cff + + - name: Ensure version matches package.json + run: | + cff_version="$(sed -n 's/^version: *//p' CITATION.cff | tr -d '"' | head -n1)" + pkg_version="$(python -c "import json; print(json.load(open('package.json'))['version'])")" + echo "CITATION.cff version: $cff_version" + echo "package.json version: $pkg_version" + if [ "$cff_version" != "$pkg_version" ]; then + echo "::error file=CITATION.cff::version ($cff_version) does not match package.json ($pkg_version). Update CITATION.cff version and date-released when bumping the release version." + exit 1 + fi + + checks: + name: Build and test + runs-on: ubuntu-22.04 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: "22" + cache: npm + cache-dependency-path: package-lock.json + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: backend/geolibre_server/pyproject.toml + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable + with: + toolchain: stable + + - name: Cache Rust dependencies + uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 + with: + workspaces: apps/geolibre-desktop/src-tauri -> target + + - name: Install Linux desktop dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + curl \ + file \ + libayatana-appindicator3-dev \ + libssl-dev \ + libwebkit2gtk-4.1-dev \ + libxdo-dev \ + patchelf \ + wget + + - name: Install frontend dependencies + run: npm ci + + - name: Install backend test dependencies + # The full test suite needs the optional engines; without them the + # vector/raster/SQL/ML tests skip themselves and CI is green but hollow. + run: python -m pip install -e "backend/geolibre_server[test]" + + # Audit the resolved backend environment for known advisories. Non-blocking + # (continue-on-error): the sidecar pulls a heavy geospatial/ML dependency + # tree whose transitive advisories are often upstream and not immediately + # fixable, so this surfaces them without halting the release cadence. + # Dependabot opens the fix PRs; this step keeps the signal visible in CI. + # + # pip-audit runs in a throwaway venv against a frozen snapshot of the test + # environment (--exclude-editable drops the local geolibre_server checkout, + # which isn't on PyPI). This keeps its own dependency resolution from + # mutating the environment the "Run CI gate" step below tests against. + - name: Audit Python dependencies (advisory, non-blocking) + continue-on-error: true + run: | + python -m pip freeze --exclude-editable > /tmp/backend-freeze.txt + python -m venv /tmp/pip-audit-venv + /tmp/pip-audit-venv/bin/pip install --quiet pip-audit + /tmp/pip-audit-venv/bin/pip-audit \ + --progress-spinner off \ + --requirement /tmp/backend-freeze.txt + + # Drive the documented `npm run ci` gate directly (lint -> build -> + # frontend+worker+backend coverage -> rust) so this workflow cannot drift + # from the script in package.json. + - name: Run CI gate (lint, build, frontend/worker/backend tests, rust) + run: npm run ci + env: + VITE_GEE_OAUTH_CLIENT_ID: ${{ secrets.VITE_GEE_OAUTH_CLIENT_ID }} diff --git a/.github/workflows/claude-code-review.yml b/.github/workflows/claude-code-review.yml new file mode 100644 index 0000000..0b9501a --- /dev/null +++ b/.github/workflows/claude-code-review.yml @@ -0,0 +1,78 @@ +name: Claude Code Review + +# Uses pull_request_target so the workflow has access to repository secrets +# (CLAUDE_CODE_OAUTH_TOKEN) even for PRs opened from forks, which lets Claude +# review and comment on ALL PRs. This is safe here because the job only reads +# the diff and posts comments. It never installs dependencies or executes the +# PR's code, so untrusted fork code is never run with the elevated token. +on: + pull_request_target: + types: [opened, synchronize, ready_for_review, reopened] + # Optional: Only run on specific file changes + # paths: + # - "src/**/*.ts" + # - "src/**/*.tsx" + # - "src/**/*.js" + # - "src/**/*.jsx" + +jobs: + claude-review: + # Optional: Filter by PR author + # if: | + # github.event.pull_request.user.login == 'external-contributor' || + # github.event.pull_request.user.login == 'new-developer' || + # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' + + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write + issues: write + id-token: write + + steps: + - name: Checkout PR head (read-only; code is reviewed, not executed) + uses: actions/checkout@v6 + with: + # pull_request_target defaults to the base ref; explicitly check out + # the PR head so Claude reviews the proposed changes. + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 1 + + - name: Run Claude Code Review + id: claude-review + uses: anthropics/claude-code-action@v1 + with: + # Use the workflow's GITHUB_TOKEN for GitHub API calls instead of the + # default OIDC -> Claude GitHub App token exchange. That exchange + # returns "401 Invalid OIDC token" for OIDC tokens minted in a + # pull_request_target context, which broke review on fork PRs. Under + # pull_request_target GITHUB_TOKEN already carries the pull-requests + # and issues write scopes this job declares, so it can post the review + # (as github-actions[bot]). claude_code_oauth_token still authenticates + # Claude to the model. + github_token: ${{ secrets.GITHUB_TOKEN }} + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + prompt: | + Perform a thorough code review of pull request ${{ github.repository }}/pull/${{ github.event.pull_request.number }}. + + Inspect the changes with `gh pr diff` and `gh pr view`. When the diff alone is not enough to judge correctness, read the surrounding source files for context. + + Review the changed code for: + - Bugs and logic errors, including edge cases, race conditions, and missing error handling + - Security issues such as injection, unsafe input handling, and leaked secrets + - Performance problems and obvious inefficiencies + - Code quality, readability, naming, and maintainability + - Adherence to any CLAUDE.md guidelines that apply to the changed files + + Concentrate on the changed lines, but use repository context to judge whether they are correct. Report findings across a range of confidence levels, not only near-certain ones. It is fine to raise a well-reasoned concern even when you are not fully certain, as long as you state your confidence and reasoning. Skip pre-existing issues unrelated to this change. + + Post specific findings as inline review comments on the relevant lines using the create_inline_comment tool. For each comment, briefly explain the issue and, when the fix is small and self-contained, include a committable suggestion block. Group minor nits together rather than posting many separate inline comments. + + After posting inline comments, post exactly one summary comment with `gh pr comment` that starts with the heading "## Code review" and lists the findings grouped by category (Bugs, Security, Performance, Quality, CLAUDE.md), each with a one-line description and confidence. If you genuinely find nothing worth raising, say so and note what you checked. + + Do not approve, merge, or modify any code. Only review and comment. Do not use web fetch; use the gh CLI for all GitHub interactions. + claude_args: | + --allowedTools "Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr comment:*),Read,Grep,Glob,mcp__github_inline_comment__create_inline_comment" + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://code.claude.com/docs/en/cli-reference for available options diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml new file mode 100644 index 0000000..e654315 --- /dev/null +++ b/.github/workflows/claude.yml @@ -0,0 +1,102 @@ +name: Claude Code + +on: + issue_comment: + types: [created] + pull_request_review_comment: + types: [created] + issues: + types: [opened, assigned] + pull_request_review: + types: [submitted] + +jobs: + # First-line gate: only proceed on an explicit @claude mention, and resolve + # the invoking user's REAL repository permission level before any token is + # granted. `author_association == 'COLLABORATOR'` is not sufficient on its + # own -- read-only and triage-only collaborators also report COLLABORATOR, so + # gating on the enum alone would let a non-push user invoke the agent. This + # job runs with a read-only token and emits `trusted=true` only for users who + # can already push (write / maintain / admin roles). + check-access: + if: | + (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) || + (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) || + (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude'))) + runs-on: ubuntu-latest + # This job only calls the REST API (via gh) and never checks out or reads + # repository content, so it needs no repository scopes. The + # collaborators/permission endpoint requires only metadata:read, which the + # GITHUB_TOKEN always retains even with an empty permissions block. + permissions: {} + outputs: + trusted: ${{ steps.check.outputs.trusted }} + steps: + - name: Resolve actor permission level + id: check + env: + GH_TOKEN: ${{ github.token }} + REPO: ${{ github.repository }} + ACTOR: ${{ github.actor }} + run: | + if role=$(gh api "repos/${REPO}/collaborators/${ACTOR}/permission" --jq '.role_name' 2>err.log); then + echo "Actor ${ACTOR} resolved to role: ${role}" + case "${role}" in + admin | maintain | write) echo "trusted=true" >> "${GITHUB_OUTPUT}" ;; + *) echo "trusted=false" >> "${GITHUB_OUTPUT}" ;; + esac + elif grep -q 'HTTP 404' err.log; then + # 404 = the actor is not a collaborator with any role: not trusted. + echo "Actor ${ACTOR} is not a repository collaborator; marking untrusted." + echo "trusted=false" >> "${GITHUB_OUTPUT}" + else + # Any other failure (auth, rate limit, network) must not be silently + # downgraded to "untrusted" -- surface it and fail the job instead. + echo "::error::Permission lookup for ${ACTOR} failed:" + cat err.log + exit 1 + fi + + claude: + needs: check-access + if: needs.check-access.outputs.trusted == 'true' + runs-on: ubuntu-latest + # Least-privilege token. Each scope maps to a concrete operation the agent + # performs, and this job only runs after check-access confirms the invoking + # user already has push access. `id-token: write` is intentionally omitted: + # this workflow authenticates with an OAuth token and has no OIDC use case. + # + # Residual risk: this gate controls WHO can invoke the agent, not WHAT it + # reads. A trusted maintainer invoking @claude on an issue/PR authored by an + # untrusted user still exposes the agent to attacker-controlled text (the + # diff or issue body) and thus to prompt injection while it holds a + # write-capable token. Review the target before invoking on untrusted input. + permissions: + contents: write # commit and push changes on a branch when asked to edit code + pull-requests: write # open/update PRs and post review replies + issues: write # post replies on the triggering issue + actions: read # read CI results on PRs + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - name: Run Claude Code + id: claude + uses: anthropics/claude-code-action@v1 + with: + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + + # This is an optional setting that allows Claude to read CI results on PRs + additional_permissions: | + actions: read + + # Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it. + # prompt: 'Update the pull request description to include a summary of changes.' + + # Optional: Add claude_args to customize behavior and configuration + # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md + # or https://code.claude.com/docs/en/cli-reference for available options + # claude_args: '--allowed-tools Bash(gh pr *)' diff --git a/.github/workflows/cloudflare-preview.yml b/.github/workflows/cloudflare-preview.yml new file mode 100644 index 0000000..63a4e47 --- /dev/null +++ b/.github/workflows/cloudflare-preview.yml @@ -0,0 +1,165 @@ +name: Cloudflare preview + +# Builds the docs site + web demo for every pull request and uploads it to a +# Cloudflare Pages project as a *preview* deployment, then comments the URL on +# the PR. This replaces the old Netlify Deploy Previews. +# +# The build mirrors .github/workflows/pages.yml (the GitHub Pages production +# deploy) so previews match what ships: +# docs deps -> jupyterlite deps -> npm ci -> web demo build -> docs build -> +# bundle demo into site/demo. +# +# NOTE on fork PRs: the `pull_request` event does NOT expose repository secrets +# to PRs opened from forks, so previews only run for branches pushed to this +# repo (maintainers and collaborators). External fork PRs are skipped — the job +# guard below short-circuits them instead of failing with a missing token. +# +# Required repository secrets (already present for the Workers deploys): +# CLOUDFLARE_API_TOKEN - needs the "Cloudflare Pages: Edit" permission +# CLOUDFLARE_ACCOUNT_ID +# Optional (baked into the client bundle, same as pages.yml): +# VITE_GEE_OAUTH_CLIENT_ID, VITE_PROTOMAPS_API_KEY, +# VITE_GOOGLE_MAPS_API_KEY or GOOGLE_MAPS_API_KEY + +on: + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + pull-requests: write + +concurrency: + group: cf-preview-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + preview: + name: Build and deploy preview + runs-on: ubuntu-latest + # Skip fork PRs: secrets are unavailable there, so the deploy would fail. + if: github.event_name == 'workflow_dispatch' || github.event.pull_request.head.repo.full_name == github.repository + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + # Match ci.yml: don't leave the GITHUB_TOKEN in .git/config where an + # npm lifecycle script during `npm ci`/`npm run build` could read it. + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: | + requirements-docs.txt + apps/geolibre-desktop/jupyterlite/requirements.txt + + - name: Install documentation dependencies + run: python -m pip install -r requirements-docs.txt + + - name: Install JupyterLite build dependencies + run: python -m pip install -r apps/geolibre-desktop/jupyterlite/requirements.txt + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: lts/* + cache: npm + cache-dependency-path: package-lock.json + + - name: Install frontend dependencies + run: npm ci + + - name: Build web demo + run: npm run build -w geolibre-desktop + env: + GEOLIBRE_APP_BASE: ./ + VITE_GEE_OAUTH_CLIENT_ID: ${{ secrets.VITE_GEE_OAUTH_CLIENT_ID }} + VITE_PROTOMAPS_API_KEY: ${{ secrets.VITE_PROTOMAPS_API_KEY }} + VITE_GOOGLE_MAPS_API_KEY: ${{ secrets.VITE_GOOGLE_MAPS_API_KEY }} + GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }} + VITE_MAPILLARY_ACCESS_TOKEN: ${{ secrets.VITE_MAPILLARY_ACCESS_TOKEN || vars.VITE_MAPILLARY_ACCESS_TOKEN }} + VITE_GEOLIBRE_COLLAB_URL: wss://collab.geolibre.app + + - name: Build documentation + run: zensical build --strict + + - name: Add web demo to site + run: | + mkdir -p site/demo + cp -R apps/geolibre-desktop/dist/. site/demo/ + test -f site/demo/index.html + test -f site/demo/jupyterlite/lab/index.html + + - name: Offload oversized DuckDB WASM to CDN + # Cloudflare Pages rejects any single file > 25 MiB. The bundled DuckDB + # WASM modules (duckdb-eh ~35 MiB, duckdb-mvp ~40 MiB) exceed that, so we + # drop the local copies and redirect their hashed URLs to jsDelivr at the + # exact pinned version (byte-identical to the bundled file). This only + # affects the Cloudflare preview; GitHub Pages still serves them locally. + # Any OTHER oversized file is a hard error so we never ship a broken site. + run: | + set -euo pipefail + version=$(node -e "console.log(require('./node_modules/@duckdb/duckdb-wasm/package.json').version)") + echo "duckdb-wasm version: $version" + redirects=site/_redirects + touch "$redirects" + moved=0 + # 26214400 bytes = 25 MiB, Cloudflare Pages' exact per-file limit. + while IFS= read -r -d '' f; do + rel=${f#site} + base=$(basename "$f") + case "$base" in + duckdb-eh-*) cdn=duckdb-eh.wasm ;; + duckdb-mvp-*) cdn=duckdb-mvp.wasm ;; + duckdb-coi-*) cdn=duckdb-coi.wasm ;; + *) + echo "::error::Oversized file not handled by CDN redirect: $rel ($(du -h "$f" | cut -f1)). Cloudflare Pages rejects files > 25 MiB." + exit 1 ;; + esac + echo "$rel https://cdn.jsdelivr.net/npm/@duckdb/duckdb-wasm@${version}/dist/${cdn} 302" >> "$redirects" + rm "$f" + echo "redirect: $rel -> $cdn" + moved=$((moved + 1)) + done < <(find site -type f -size +26214400c -print0) + echo "offloaded $moved oversized file(s)" + echo "----- site/_redirects -----" + cat "$redirects" + + - name: Deploy to Cloudflare Pages + id: deploy + uses: cloudflare/wrangler-action@v4 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + # --branch is anything other than the project's production branch, so + # every run here is a preview deployment with its own URL. + command: >- + pages deploy site + --project-name=geolibre-preview + --branch="${{ github.head_ref || github.ref_name }}" + --commit-dirty=true + + - name: Comment preview URL on PR + if: github.event_name == 'pull_request' + uses: actions/github-script@v9 + env: + DEPLOY_URL: ${{ steps.deploy.outputs.deployment-url }} + with: + script: | + const url = process.env.DEPLOY_URL; + if (!url) return; + const marker = ''; + const body = `${marker}\n### ⚡ Cloudflare Pages preview\n\n| Item | Value |\n| --- | --- |\n| Preview | ${url} |\n| Demo app | ${url}/demo/ |\n| Commit | \`${context.sha.slice(0, 7)}\` |`; + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + const { data: comments } = await github.rest.issues.listComments({ owner, repo, issue_number, per_page: 100 }); + const existing = comments.find((c) => c.body && c.body.includes(marker)); + if (existing) { + await github.rest.issues.updateComment({ owner, repo, comment_id: existing.id, body }); + } else { + await github.rest.issues.createComment({ owner, repo, issue_number, body }); + } diff --git a/.github/workflows/deploy-collab.yml b/.github/workflows/deploy-collab.yml new file mode 100644 index 0000000..1d26a8f --- /dev/null +++ b/.github/workflows/deploy-collab.yml @@ -0,0 +1,31 @@ +name: Deploy collab + +on: + push: + branches: [main] + paths: + - "workers/collab/**" + - ".github/workflows/deploy-collab.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: deploy-collab-${{ github.ref }} + cancel-in-progress: true + +jobs: + deploy: + name: Deploy collaboration relay to Cloudflare Workers + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Deploy to Cloudflare Workers + uses: cloudflare/wrangler-action@v4 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + workingDirectory: workers/collab diff --git a/.github/workflows/deploy-tiles.yml b/.github/workflows/deploy-tiles.yml new file mode 100644 index 0000000..2e7d2ed --- /dev/null +++ b/.github/workflows/deploy-tiles.yml @@ -0,0 +1,47 @@ +name: Deploy tiles + +on: + push: + branches: [main] + paths: + - "workers/tiles/**" + - ".github/workflows/deploy-tiles.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: deploy-tiles-${{ github.ref }} + cancel-in-progress: true + +jobs: + deploy: + name: Deploy planetary tile proxy to Cloudflare Workers + runs-on: ubuntu-latest + # workflow_dispatch can target any branch; only ever deploy main to + # production so a manual run can't publish unmerged code. + if: github.ref == 'refs/heads/main' + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + # Unlike the viewer/collab workers, this one has a real runtime dependency + # (upng-js) that wrangler must bundle, so the workspace deps have to be + # installed before deploy. wrangler-action does not install for us. + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: "22" + cache: npm + cache-dependency-path: package-lock.json + + - name: Install dependencies + run: npm ci + + - name: Deploy to Cloudflare Workers + uses: cloudflare/wrangler-action@v4 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + workingDirectory: workers/tiles diff --git a/.github/workflows/deploy-viewer.yml b/.github/workflows/deploy-viewer.yml new file mode 100644 index 0000000..6b71e7b --- /dev/null +++ b/.github/workflows/deploy-viewer.yml @@ -0,0 +1,31 @@ +name: Deploy viewer + +on: + push: + branches: [main] + paths: + - "workers/viewer/**" + - ".github/workflows/deploy-viewer.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: deploy-viewer-${{ github.ref }} + cancel-in-progress: true + +jobs: + deploy: + name: Deploy viewer proxy to Cloudflare Workers + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Deploy to Cloudflare Workers + uses: cloudflare/wrangler-action@v4 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + workingDirectory: workers/viewer diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml new file mode 100644 index 0000000..7ebe74e --- /dev/null +++ b/.github/workflows/labels.yml @@ -0,0 +1,27 @@ +name: Sync labels + +# Keeps the repository's labels in sync with .github/labels.yml so they are +# version-controlled and recreated automatically if deleted. The sync is +# additive: existing labels not listed in the manifest are left untouched. + +on: + push: + branches: [main] + paths: + - .github/labels.yml + - .github/workflows/labels.yml + workflow_dispatch: + +permissions: + issues: write + +jobs: + sync: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: EndBug/label-sync@v2 + with: + config-file: .github/labels.yml + delete-other-labels: false + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/msix-store.yml b/.github/workflows/msix-store.yml new file mode 100644 index 0000000..94bcda0 --- /dev/null +++ b/.github/workflows/msix-store.yml @@ -0,0 +1,55 @@ +name: Build Store MSIX + +# Manually builds the Windows MSIX package for a Microsoft Store submission and +# uploads it as a workflow artifact. This does NOT touch any GitHub release; it +# exists so a Store-ready package can be produced on demand (e.g. to fix a +# rejected submission) without re-publishing a release. + +on: + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + name: Build MSIX + runs-on: windows-2025 + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: lts/* + cache: npm + cache-dependency-path: package-lock.json + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + + - name: Install frontend dependencies + run: npm ci + + - name: Build Tauri release binary + env: + NODE_OPTIONS: --max-old-space-size=4096 + VITE_GEE_OAUTH_CLIENT_ID: ${{ secrets.VITE_GEE_OAUTH_CLIENT_ID }} + # Strip the in-app "Check for updates" flow from this Store-only build + # so the package updates solely through the Store (Microsoft policy + # 10.2.5). Set only here — the release.yml installers keep the updater. + GEOLIBRE_STORE_BUILD: "1" + run: npm run tauri:build -- --no-sign + + - name: Build MSIX package + id: build_msix + shell: pwsh + run: ./packaging/msix/build-msix.ps1 + + - name: Upload MSIX artifact + uses: actions/upload-artifact@v4 + with: + name: geolibre-store-msix + path: ${{ steps.build_msix.outputs.msix_path }} + if-no-files-found: error diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..0e62971 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,105 @@ +name: Deploy Website + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + name: Build website and demo + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + cache: pip + cache-dependency-path: | + requirements-docs.txt + apps/geolibre-desktop/jupyterlite/requirements.txt + + - name: Install documentation dependencies + run: python -m pip install -r requirements-docs.txt + + - name: Install JupyterLite build dependencies + # Lets the web build's `prebuild` hook generate the self-hosted + # JupyterLite site (served in the Notebook panel); without these the step + # skips and the demo's notebook 404s on the published site. + run: python -m pip install -r apps/geolibre-desktop/jupyterlite/requirements.txt + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: lts/* + cache: npm + cache-dependency-path: package-lock.json + + - name: Install frontend dependencies + run: npm ci + + - name: Build web demo + run: npm run build -w geolibre-desktop + env: + GEOLIBRE_APP_BASE: ./ + VITE_GEE_OAUTH_CLIENT_ID: ${{ secrets.VITE_GEE_OAUTH_CLIENT_ID }} + # Protomaps API key, baked into the client bundle so the New map + # dialog can offer Protomaps basemaps. When unset, those options are + # hidden (see getProtomapsStyleUrl in @geolibre/core). + VITE_PROTOMAPS_API_KEY: ${{ secrets.VITE_PROTOMAPS_API_KEY }} + # Google Photorealistic 3D Tiles and Google Street View use the same + # Maps key. Prefer the Vite-prefixed secret name, but also pass the + # bare name below so vite.config.ts can expose local/CI test setups. + VITE_GOOGLE_MAPS_API_KEY: ${{ secrets.VITE_GOOGLE_MAPS_API_KEY }} + GOOGLE_MAPS_API_KEY: ${{ secrets.GOOGLE_MAPS_API_KEY }} + # Mapillary access token, baked into the client bundle so the + # Mapillary/Street View plugins work without each visitor pasting a + # token. It ends up publicly readable in the served JS, so it is not a + # true secret; a repo variable works as well as a secret here. + VITE_MAPILLARY_ACCESS_TOKEN: ${{ secrets.VITE_MAPILLARY_ACCESS_TOKEN || vars.VITE_MAPILLARY_ACCESS_TOKEN }} + # Public relay URL (baked into the client bundle), not a secret. Lights + # up live collaboration; requires the workers/collab relay to be + # deployed (see docs/collaboration.md). + VITE_GEOLIBRE_COLLAB_URL: wss://collab.geolibre.app + + - name: Build documentation + run: zensical build --strict + + - name: Add web demo to site + run: | + mkdir -p site/demo + cp -R apps/geolibre-desktop/dist/. site/demo/ + test -f site/demo/index.html + test -f site/demo/jupyterlite/lab/index.html + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v5 + with: + path: site + + deploy: + name: Deploy to GitHub Pages + if: github.event_name != 'pull_request' + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v5 diff --git a/.github/workflows/publish-container.yml b/.github/workflows/publish-container.yml new file mode 100644 index 0000000..ec0873e --- /dev/null +++ b/.github/workflows/publish-container.yml @@ -0,0 +1,70 @@ +name: Publish Container Image + +on: + push: + branches: [main] + tags: ["v*"] + pull_request: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + packages: write + +concurrency: + group: container-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +jobs: + build: + name: Build and publish container image + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set image name + id: image + run: echo "name=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT" + + - name: Set up QEMU + if: github.event_name != 'pull_request' + uses: docker/setup-qemu-action@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract Docker metadata + id: metadata + uses: docker/metadata-action@v5 + with: + images: ${{ steps.image.outputs.name }} + tags: | + type=ref,event=branch + type=ref,event=tag + type=sha,prefix=sha- + type=raw,value=latest,enable={{is_default_branch}} + + - name: Build and publish image + uses: docker/build-push-action@v7 + with: + context: . + file: Dockerfile + push: ${{ github.event_name != 'pull_request' }} + platforms: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }} + tags: ${{ steps.metadata.outputs.tags }} + labels: ${{ steps.metadata.outputs.labels }} + build-args: | + VITE_GEE_OAUTH_CLIENT_ID=${{ secrets.VITE_GEE_OAUTH_CLIENT_ID }} + VITE_MAPILLARY_ACCESS_TOKEN=${{ secrets.VITE_MAPILLARY_ACCESS_TOKEN || vars.VITE_MAPILLARY_ACCESS_TOKEN }} + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/publish-python.yml b/.github/workflows/publish-python.yml new file mode 100644 index 0000000..b652afe --- /dev/null +++ b/.github/workflows/publish-python.yml @@ -0,0 +1,99 @@ +name: Publish Python package + +on: + release: + types: [published] + pull_request: + branches: [main] + paths: + - "python/**" + - "apps/geolibre-desktop/src/**" + - "packages/**" + - "scripts/build-embed.mjs" + - ".github/workflows/publish-python.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: publish-python-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: Test Python package + runs-on: ubuntu-22.04 + strategy: + matrix: + # Cover the declared minimum (requires-python >= 3.11) and current + # releases so newer-only syntax can't slip in unnoticed. + python-version: ["3.11", "3.12", "3.13", "3.14"] + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: ${{ matrix.python-version }} + + # Tests cover the pure-Python builders and do not need the bundled web + # app, so install only the runtime/test deps and run against the sources. + - name: Install test dependencies + run: python -m pip install anywidget traitlets pytest + + - name: Run tests + run: python -m pytest python/tests + env: + PYTHONPATH: python/src + + publish: + name: Build wheel and publish + runs-on: ubuntu-22.04 + needs: test + if: github.event_name == 'release' + environment: pypi + permissions: + contents: read + id-token: write # PyPI Trusted Publishing (OIDC) + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: "22" + cache: npm + cache-dependency-path: package-lock.json + + - name: Set up Python + uses: actions/setup-python@v6 + with: + python-version: "3.12" + + - name: Install frontend dependencies + run: npm ci + + - name: Install build backend + run: python -m pip install build + + - name: Build wheel and sdist + working-directory: python + run: python -m build + env: + # Force a fresh embed build so the published wheel always carries the + # current app bundle. + GEOLIBRE_FORCE_JS_BUILD: "1" + NODE_OPTIONS: --max-old-space-size=4096 + VITE_GEE_OAUTH_CLIENT_ID: ${{ secrets.VITE_GEE_OAUTH_CLIENT_ID }} + + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1 + with: + packages-dir: python/dist diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..bd08dc6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,361 @@ +name: Release Desktop App + +on: + release: + types: [published] + +permissions: + contents: write + +concurrency: + group: release-${{ github.event.release.tag_name }} + cancel-in-progress: false + +jobs: + build: + name: Build ${{ matrix.name }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - name: Linux x64 + os: ubuntu-22.04 + args: --bundles deb,rpm,appimage + rust-targets: "" + + - name: Windows x64 + os: windows-2025 + args: --no-sign + rust-targets: "" + + # macOS bundles are Developer ID signed and notarized via the + # APPLE_* secrets wired into the tauri-action step below, so users can + # open the DMG without stripping the quarantine attribute. Do not pass + # --no-sign here. + - name: macOS Apple Silicon + os: macos-latest + args: --target aarch64-apple-darwin + rust-targets: aarch64-apple-darwin + + - name: macOS Intel + os: macos-latest + args: --target x86_64-apple-darwin + rust-targets: x86_64-apple-darwin + + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Set up Node.js + uses: actions/setup-node@v6 + with: + node-version: lts/* + cache: npm + cache-dependency-path: package-lock.json + + - name: Install Rust stable + uses: dtolnay/rust-toolchain@stable + + - name: Install macOS Rust target + if: matrix.rust-targets != '' + run: rustup target add ${{ matrix.rust-targets }} + + - name: Install Linux dependencies + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + curl \ + file \ + libayatana-appindicator3-dev \ + libfuse2 \ + librsvg2-dev \ + libssl-dev \ + libwebkit2gtk-4.1-dev \ + libxdo-dev \ + patchelf \ + rpm \ + wget + + - name: Install frontend dependencies + run: npm ci + + - name: Build and upload release assets + uses: tauri-apps/tauri-action@v0.6 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + NODE_OPTIONS: --max-old-space-size=4096 + VITE_GEE_OAUTH_CLIENT_ID: ${{ secrets.VITE_GEE_OAUTH_CLIENT_ID }} + # macOS Developer ID signing + notarization. The secrets only resolve + # to real values on macOS runners; Linux/Windows get empty strings so + # the credentials are never placed in their process environment. + # APPLE_SIGNING_IDENTITY defaults to "-" when the secret is absent + # (e.g. a fork without secrets) so macOS still ad-hoc signs rather + # than failing the build now that tauri.conf.json no longer pins it. + APPLE_CERTIFICATE: ${{ runner.os == 'macOS' && secrets.APPLE_CERTIFICATE || '' }} + APPLE_CERTIFICATE_PASSWORD: ${{ runner.os == 'macOS' && secrets.APPLE_CERTIFICATE_PASSWORD || '' }} + APPLE_SIGNING_IDENTITY: ${{ runner.os == 'macOS' && (secrets.APPLE_SIGNING_IDENTITY || '-') || '' }} + APPLE_ID: ${{ runner.os == 'macOS' && secrets.APPLE_ID || '' }} + APPLE_PASSWORD: ${{ runner.os == 'macOS' && secrets.APPLE_PASSWORD || '' }} + APPLE_TEAM_ID: ${{ runner.os == 'macOS' && secrets.APPLE_TEAM_ID || '' }} + with: + projectPath: apps/geolibre-desktop + releaseId: ${{ github.event.release.id }} + tagName: ${{ github.event.release.tag_name }} + args: ${{ matrix.args }} + uploadPlainBinary: true + + - name: Build MSIX package + id: build_msix + if: runner.os == 'Windows' + shell: pwsh + run: ./packaging/msix/build-msix.ps1 + + - name: Upload MSIX release asset + if: runner.os == 'Windows' + shell: pwsh + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + $msix = "${{ steps.build_msix.outputs.msix_path }}" + if (-not (Test-Path $msix)) { + throw "No MSIX package was created at '$msix'." + } + gh release upload "${{ github.event.release.tag_name }}" "$msix" --clobber + + - name: Build portable zip + id: build_portable + if: runner.os == 'Windows' + shell: pwsh + run: ./packaging/portable/build-portable.ps1 + + - name: Upload portable zip release asset + if: runner.os == 'Windows' + shell: pwsh + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + $portable = "${{ steps.build_portable.outputs.portable_path }}" + if (-not (Test-Path $portable)) { + throw "No portable zip was created at '$portable'." + } + gh release upload "${{ github.event.release.tag_name }}" "$portable" --clobber + + homebrew: + name: Update Homebrew cask + needs: build + runs-on: ubuntu-latest + # The primary cask is in the official homebrew/homebrew-cask repo, which + # BrewTestBot auto-bumps via livecheck. This job only updates the self-hosted + # tap (opengeos/homebrew-geolibre), kept as a fallback. + # Serialise tap pushes so two close releases can't race on a non-fast-forward. + concurrency: + group: homebrew-tap-update + cancel-in-progress: false + # Only update the tap for full releases, not prereleases. + if: ${{ !github.event.release.prerelease }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Compute DMG checksums + id: checksums + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.event.release.tag_name }} + run: | + version="${TAG#v}" + arm="GeoLibre.Desktop_${version}_aarch64.dmg" + intel="GeoLibre.Desktop_${version}_x64.dmg" + gh release download "$TAG" --repo "$GITHUB_REPOSITORY" \ + --pattern "$arm" --pattern "$intel" --dir dmgs + { + echo "version=$version" + echo "sha_arm=$(sha256sum "dmgs/$arm" | awk '{print $1}')" + echo "sha_intel=$(sha256sum "dmgs/$intel" | awk '{print $1}')" + } >> "$GITHUB_OUTPUT" + + - name: Check out Homebrew tap + uses: actions/checkout@v6 + with: + repository: opengeos/homebrew-geolibre + token: ${{ secrets.HOMEBREW_TAP_TOKEN }} + path: tap + persist-credentials: false + + - name: Render and commit cask + env: + VERSION: ${{ steps.checksums.outputs.version }} + SHA256_ARM: ${{ steps.checksums.outputs.sha_arm }} + SHA256_INTEL: ${{ steps.checksums.outputs.sha_intel }} + HOMEBREW_TAP_TOKEN: ${{ secrets.HOMEBREW_TAP_TOKEN }} + run: | + mkdir -p tap/Casks + scripts/render-homebrew-cask.sh > tap/Casks/geolibre.rb + cd tap + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Casks/geolibre.rb + # --cached so an initially untracked cask (fresh tap) is still detected. + if git diff --cached --quiet; then + echo "Cask already up to date for ${VERSION}." + exit 0 + fi + git commit -m "geolibre ${VERSION}" + git push "https://x-access-token:${HOMEBREW_TAP_TOKEN}@github.com/opengeos/homebrew-geolibre.git" HEAD:main + + aur: + name: Publish AUR package + needs: build + runs-on: ubuntu-latest + # Fully isolated from the asset build and the Homebrew job: it only `needs` + # build for the .deb, nothing depends on it, and continue-on-error means a + # failed AUR push leaves the release (and its other publish targets) green. + continue-on-error: true + # Serialise AUR pushes so two close releases can't race on a non-fast-forward. + concurrency: + group: aur-publish + cancel-in-progress: false + # Only publish for full releases, not prereleases. + if: ${{ !github.event.release.prerelease }} + env: + # Surfaced as an env var so steps can skip cleanly when the secret is + # absent (forks, or before the maintainer configures it). + AUR_SSH_PRIVATE_KEY: ${{ secrets.AUR_SSH_PRIVATE_KEY }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Render PKGBUILD from the release .deb + if: ${{ env.AUR_SSH_PRIVATE_KEY != '' }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.event.release.tag_name }} + run: | + version="${TAG#v}" + deb="GeoLibre.Desktop_${version}_amd64.deb" + gh release download "$TAG" --repo "$GITHUB_REPOSITORY" --pattern "$deb" --dir debs + sha="$(sha256sum "debs/$deb" | awk '{print $1}')" + mkdir -p aur + VERSION="$version" SHA256_AMD64="$sha" REPO="$GITHUB_REPOSITORY" \ + scripts/render-aur-pkgbuild.sh > aur/PKGBUILD + echo "----- rendered PKGBUILD -----" + cat aur/PKGBUILD + + - name: Publish to the AUR + if: ${{ env.AUR_SSH_PRIVATE_KEY != '' }} + # Pinned to a commit SHA (not a mutable tag) because this step receives + # the AUR SSH private key. Update deliberately when bumping the version. + uses: KSXGitHub/github-actions-deploy-aur@da03e160361ce01bf087e790b6ffd196d7dccff7 # v4.1.3 + with: + pkgname: geolibre-bin + pkgbuild: aur/PKGBUILD + commit_username: github-actions[bot] + commit_email: github-actions[bot]@users.noreply.github.com + ssh_private_key: ${{ env.AUR_SSH_PRIVATE_KEY }} + commit_message: "Update to ${{ github.event.release.tag_name }}" + allow_empty_commits: false + updpkgsums: false + + copr: + name: Publish Fedora COPR package + needs: build + runs-on: ubuntu-latest + # Same isolation as the AUR job: only needs build for the .rpm, nothing + # depends on it, continue-on-error keeps a COPR failure from reddening the + # release, and it skips itself when the API token is absent (forks). + continue-on-error: true + concurrency: + group: copr-publish + cancel-in-progress: false + if: ${{ !github.event.release.prerelease }} + env: + COPR_API_TOKEN: ${{ secrets.COPR_API_TOKEN }} + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Build SRPM and submit to COPR + if: ${{ env.COPR_API_TOKEN != '' }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG: ${{ github.event.release.tag_name }} + COPR_PROJECT: giswqs/geolibre + run: | + version="${TAG#v}" + # Release date (YYYY-MM-DD) for the metainfo and %changelog. + release_date="$(gh release view "$TAG" --repo "$GITHUB_REPOSITORY" \ + --json publishedAt --jq '.publishedAt' | cut -dT -f1)" + + sudo apt-get update -qq + sudo apt-get install -y -qq rpm appstream + pipx install copr-cli + # copr-cli imports `rich` but does not always declare it as a dependency. + pipx inject copr-cli rich + + # COPR API token config from the secret. Create it with restrictive + # permissions atomically (umask), not chmod-after-write. + mkdir -p ~/.config + (umask 177; printf '%s\n' "$COPR_API_TOKEN" > ~/.config/copr) + + mkdir -p ~/rpmbuild/SPECS ~/rpmbuild/SOURCES + VERSION="$version" DATE="$release_date" \ + scripts/render-linux-metainfo.sh > ~/rpmbuild/SOURCES/org.geolibre.desktop.metainfo.xml + VERSION="$version" DATE="$release_date" REPO="$GITHUB_REPOSITORY" \ + scripts/render-copr-spec.sh > ~/rpmbuild/SPECS/geolibre.spec + # The spec ships the project license as Source2. + cp LICENSE ~/rpmbuild/SOURCES/ + + # Validate the metainfo in CI, independently of COPR's mock build. + appstreamcli validate --no-net ~/rpmbuild/SOURCES/org.geolibre.desktop.metainfo.xml + + # The upstream RPM is Source0; rpmbuild needs it locally to build the + # SRPM (it does not fetch URLs). + gh release download "$TAG" --repo "$GITHUB_REPOSITORY" \ + --pattern "GeoLibre.Desktop-${version}-1.x86_64.rpm" --dir ~/rpmbuild/SOURCES + # Log the checksum of the repackaged RPM for auditability. + sha256sum ~/rpmbuild/SOURCES/"GeoLibre.Desktop-${version}-1.x86_64.rpm" \ + | tee -a "$GITHUB_STEP_SUMMARY" + + srpm="$(rpmbuild -bs ~/rpmbuild/SPECS/geolibre.spec \ + --define "_topdir $HOME/rpmbuild" | sed -n 's/^Wrote: //p')" + [[ -n "$srpm" ]] || { echo "::error::rpmbuild produced no SRPM path"; exit 1; } + echo "Built SRPM: $srpm" + copr-cli build --nowait "$COPR_PROJECT" "$srpm" + + winget: + name: Publish to winget + needs: build + runs-on: ubuntu-latest + # Isolated like the AUR/COPR jobs: only needs build for the release assets, + # nothing depends on it, continue-on-error keeps a winget hiccup from + # failing the release, and it skips itself when WINGET_TOKEN is unset (forks, + # or before the maintainer configures it). + continue-on-error: true + concurrency: + group: winget-publish + cancel-in-progress: false + # Only publish for full releases, not prereleases. + if: ${{ !github.event.release.prerelease }} + env: + WINGET_TOKEN: ${{ secrets.WINGET_TOKEN }} + steps: + - name: Submit OpenGeos.GeoLibre to winget-pkgs + if: ${{ env.WINGET_TOKEN != '' }} + # Pinned to a commit SHA (not the mutable v2 tag) because this step + # receives a token with access to the winget-pkgs fork. winget-releaser + # derives the version from the release tag and uses komac under the hood. + uses: vedantmgoyal9/winget-releaser@4ffc7888bffd451b357355dc214d43bb9f23917e # v2 + with: + identifier: OpenGeos.GeoLibre + installers-regex: '_x64-setup\.exe$' + fork-user: giswqs + token: ${{ env.WINGET_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9c6d70f --- /dev/null +++ b/.gitignore @@ -0,0 +1,65 @@ +node_modules/ +dist/ +dist-embed/ +# Generated by `npm run build:jupyterlite` (best-effort prebuild); not committed. +apps/geolibre-desktop/public/jupyterlite/ +# JupyterLite (doit) build cache, written to the working dir during the build. +.jupyterlite.doit.db +# The kernel-side geolibre client and the Welcome example are staged here from +# their canonical backend copies by scripts/build-jupyterlite.mjs. +apps/geolibre-desktop/jupyterlite/files/geolibre.py +apps/geolibre-desktop/jupyterlite/files/Welcome.ipynb +site/ +target/ +.tauri/ +*.log +.DS_Store +.env +.env.local +__pycache__/ +*.pyc + +# Generated by the copy-vector-ops Vite plugin from the backend source of truth +# (backend/geolibre_server/geolibre_server/vector_ops.py). +apps/geolibre-desktop/src/lib/pyodide/vector_ops.generated.py + +# Generated by the copy-rtl-text Vite plugin from @mapbox/mapbox-gl-rtl-text. +apps/geolibre-desktop/src/lib/vendor/mapbox-gl-rtl-text.generated.js + +# Generated by the copy-cesium-assets Vite plugin from node_modules/cesium. +apps/geolibre-desktop/public/cesium/ +.venv/ +*.egg-info/ +.pytest_cache/ +# Coverage artifacts (pytest-cov data file, HTML/XML reports, c8 output) +.coverage +.coverage.* +coverage/ +htmlcov/ +coverage.xml +.idea/ +.vscode/ +*.tsbuildinfo +**/src-tauri/gen/ +.playwright-cli/ +.claude/ +uv.lock + +# Playwright E2E artifacts +test-results/ +playwright-report/ +blob-report/ +.playwright/ +private/ + +# Documentation screenshots are hosted on Cloudflare R2 (data.geolibre.app/images), +# not committed to the repo, to save space. +docs/assets/screenshots/ +python/examples/my-map.geolibre.json + +# Cloudflare Workers local dev state (wrangler dev / miniflare) +.wrangler/ + +# Rendered Homebrew cask, emitted to the working dir by scripts/render-*-cask.sh +# for submission to a tap / homebrew-cask; not committed to this repo. +/geolibre.rb diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..9e280f4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,40 @@ +ci: + skip: [npm-build, eslint] + +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v6.0.0 + hooks: + - id: check-json + # This E2E fixture is intentionally malformed JSON — it drives the + # error-handling spec that asserts the app rejects a broken drop. + exclude: ^e2e/fixtures/malformed\.geojson$ + - id: check-added-large-files + args: ["--maxkb=1024", "--enforce-all"] + - id: check-merge-conflict + - id: check-toml + - id: check-yaml + args: ["--unsafe"] + - id: end-of-file-fixer + - id: mixed-line-ending + args: ["--fix=lf"] + - id: trailing-whitespace + + - repo: local + hooks: + - id: eslint + name: eslint + entry: npm run lint + language: system + pass_filenames: false + types_or: [ts, tsx, javascript, jsx] + - id: npm-build + name: npm build + entry: npm run build + language: system + pass_filenames: false + + - repo: https://github.com/kynan/nbstripout + rev: 0.9.1 + hooks: + - id: nbstripout diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..b7b942e --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,24 @@ +cff-version: 1.2.0 +title: GeoLibre +message: "If you use this software, please cite it using the metadata from this file." +type: software +abstract: "GeoLibre is a free and open-source, lightweight, cloud-native GIS platform for visualizing, exploring, and analyzing geospatial data across web browsers, desktop applications, mobile devices, and Jupyter notebooks while keeping data local and private." +authors: + - given-names: Qiusheng + family-names: Wu + email: giswqs@gmail.com + affiliation: "University of Tennessee, Knoxville, USA" + orcid: "https://orcid.org/0000-0001-5437-4073" +version: 2.0.0 +date-released: 2026-07-10 +doi: 10.5281/zenodo.20785400 +repository-code: "https://github.com/opengeos/GeoLibre" +url: "https://geolibre.app" +license: MIT +keywords: + - GIS + - geospatial + - remote sensing + - data visualization + - MapLibre + - Python diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..8123859 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,94 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Repository shape + +GeoLibre is a single **npm workspaces monorepo** (`apps/*`, `packages/*`, `workers/*`) plus two non-npm components: a Python FastAPI sidecar (`backend/geolibre_server`) and a separate Python package (`python/`, the `geolibre` Jupyter anywidget). One `npm install` at the root wires up every JS workspace. Use **npm** (the repo tracks `package-lock.json`), Node **22+**. + +The same React app ships three ways: native desktop via **Tauri v2** (`apps/geolibre-desktop/src-tauri`), a browser web build served by nginx (Docker), and embedded in Jupyter (the `python/` package bundles a build of the web app into its wheel). + +## Commands + +```bash +npm run dev # web dev server → http://localhost:5173 +npm run tauri:dev # desktop app (required for filesystem dialogs, local MBTiles, local raster reads) +npm run build # production web build → apps/geolibre-desktop/dist/ +npm run tauri:build # desktop installers → apps/geolibre-desktop/src-tauri/target/release/bundle/ +npm run typecheck # alias for the full build (tsc -b && vite build) — writes to dist/, not a pure type-check +npm run ci # full local gate: build + frontend + worker + backend + rust check +``` + +Tests: + +```bash +npm run test:frontend # node --test over tests/*.test.ts (tsx loader) +npm run test:frontend:coverage # same, plus a per-file coverage summary (Node built-in) +node --import tsx --test tests/.test.ts # a single frontend test file +npm run test:backend # pytest backend/geolibre_server/tests +npm run test:backend:coverage # same, plus a pytest-cov term-missing report +python -m pytest backend/geolibre_server/tests/test_x.py::test_y # a single backend test +npm run test:worker # typecheck workers/viewer +npm run test:e2e # Playwright smoke tests (e2e/) against the built web app +npm run check:rust # cargo check the Tauri crate +``` + +The `:coverage` variants run the same suites and print a coverage summary; CI +runs them so every build reports coverage. They are now **gated on a floor**: +`test:frontend:coverage` fails below 78% lines / 78% branches / 63% functions, +and `test:backend:coverage` fails below 55% (`--cov-fail-under`). The floors sit +a few points under the current numbers as a **ratchet** — regressions fail CI, +and when coverage rises comfortably above a floor, raise the floor to lock in the +gain. The frontend +report only counts files a test actually imports, so a module with no test does +not appear at all rather than as 0%. The backend coverage run (and `npm run ci`, +which calls the `:coverage` variants) needs `pytest-cov` from the backend `dev` +extra. Install the **`test`** extra to run the *full* backend suite — without +the optional engines (geopandas/rasterio/sedona/httpx) the vector/raster/SQL/ML +tests skip themselves and CI is green but hollow: +`pip install -e "backend/geolibre_server[test]"`. + +`npm run test:e2e` builds the web app, serves it with `vite preview`, and drives +it with Playwright (`@playwright/test`). First run: `npx playwright install +chromium`. The webServer reuses an already-running preview locally and rebuilds +in CI; add specs under `e2e/`. + +Dependencies are watched two ways: **Dependabot** (`.github/dependabot.yml`) +opens grouped weekly update PRs for npm, pip (backend + `python/`), cargo, and +Actions, and the CI **`audit` job** runs `npm audit --audit-level=high` +(blocking) plus a non-blocking `pip-audit` of the resolved backend environment. + +The `python/` package has its own pytest suite (`cd python && pytest`) and is built into a wheel via `npm run build:embed` (produces `apps/geolibre-desktop/dist-embed`, consumed by `python/hatch_build.py`). Its version is dynamic, sourced from `python/src/geolibre/__init__.py`. + +## Pre-commit + +`.pre-commit-config.yaml` includes a **local `npm-build` hook**, so `pre-commit run` compiles the whole app — it is slow and can touch unrelated build state. Scope it to the files you changed: `pre-commit run --files `. Run it before pushing. + +## Architecture (the parts that span files) + +The app is **store-driven**. `@geolibre/core` holds the Zustand store, domain types, and the `.geolibre.json` project schema — it is the single source of truth. Data flows one way: + +1. Data enters through the Add Data menus, Tauri dialogs, the browser file picker, drag-and-drop, or a plugin control. +2. Local vector files that MapLibre can't render directly are converted to GeoJSON in-browser by **DuckDB-WASM Spatial** (`INSTALL spatial; LOAD spatial;` → `ST_Read`; GeoParquet via the Parquet reader; zipped Shapefiles via `shpjs` with a DuckDB fallback; KMZ unzipped client-side). The result calls `addGeoJsonLayer`. +3. Tile/service/raster/ArcGIS/MBTiles/plugin layers become `GeoLibreLayer` records. +4. `MapCanvas` subscribes to `layers`; `MapController.syncLayers` (`@geolibre/map`) reconciles MapLibre sources/layers and the layer control. **You don't mutate MapLibre directly from UI** — you change store state and let sync apply it. + +Rendering is MapLibre GL JS in the webview, with **deck.gl** for raster/point-cloud/3D overlays. + +**Packages:** `@geolibre/core` (types, project format, store) · `@geolibre/map` (MapLibre lifecycle + layer sync) · `@geolibre/ui` (shadcn-style primitives) · `@geolibre/processing` (client-side algorithm registry) · `@geolibre/plugins` (plugin interface + built-in plugins) · `geolibre-desktop` (shell layout, Tauri I/O, composition). + +**Plugins:** Built-in plugins live in `packages/plugins/src/plugins/`, are exported from that package's `index.ts`, and registered in `apps/geolibre-desktop/src/hooks/usePlugins.ts`. External plugins load from zips or a `plugin.json` manifest; bundled drop-ins under `apps/geolibre-desktop/public/plugins//` bake into both web and desktop builds. See `docs/plugin-api.md`. + +**Python sidecar** (`backend/geolibre_server`, FastAPI on `127.0.0.1:8765`): backs the Whitebox toolbox, format Conversion tools, and Raster tools (rasterio). The desktop app starts it on demand. It is **optional** — Vector tools (Processing → Vector) run client-side with Turf.js and only use the sidecar's `/vector` endpoints (GeoPandas/Shapely) when the optional `vector` extra is installed; the dialog falls back to the client engine via `/vector/status`. Optional extras: `conversion`, `vector`, `raster`. Some conversions (PMTiles, Whitebox) are amd64-only. + +The browser build proxies the sidecar at `/sidecar` (same-origin, no CORS); confined to `GEOLIBRE_CONVERSION_ROOTS` (default `/data`). Local MBTiles use a custom MapLibre protocol backed by Tauri commands. + +## Conventions + +- Never commit directly to `main`; branch and open a PR. +- Tauri CSP allowlists tile/style hosts (OpenFreeMap, CARTO) — new external map/tile hosts must be added there. +- Map/tile-host CORS for selected release assets is handled by a dev-server raster proxy. +- For MapLibre control styling fixes, add scoped overrides in `apps/geolibre-desktop/src/index.css`, never edit `node_modules`. +- The Processing **menu** (`ProcessingMenu.tsx`) renders from a checked-in, auto-generated catalog, `apps/geolibre-desktop/src/lib/whitebox-menu-catalog.ts` (do not hand-edit). Whenever `geolibre-wasm` is bumped (in `packages/processing/package.json`) — including Dependabot PRs — run `node scripts/gen-whitebox-menu-catalog.mjs` and commit the result, or new/renamed WASM tools silently miss the menu (the Processing **dialog** lists them dynamically, so the gap only shows in the menu). +- UI strings are translatable via **react-i18next**; catalogs live in `apps/geolibre-desktop/src/i18n/locales/*.json` (`en.json` is the source of truth, typed by `i18next.d.ts`). Use `t()` for new user-facing strings; a `?locale`/`?lang` query param sets the embed language. See `docs/i18n.md`. +- Reference docs: `docs/architecture.md`, `docs/project-format.md`, `docs/plugin-api.md`, `docs/python.md`, `docs/i18n.md`, `docs/contributing.md`. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..7463320 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,28 @@ +# Contributing to GeoLibre + +Thanks for your interest in improving GeoLibre. The full contributing guide, +including development setup, the repository layout, the quality gate, and the +pull request workflow, lives in the documentation: + +**** (source: [`docs/contributing.md`](docs/contributing.md)) + +## Quick start + +```bash +git clone https://github.com/opengeos/GeoLibre.git +cd GeoLibre +npm install +npm run dev # web build at http://localhost:5173 +``` + +Before opening a pull request: + +```bash +pre-commit run --all-files +npm run ci +``` + +Branch off `main` (never commit to it directly), keep changes focused, follow +[Conventional Commits](https://www.conventionalcommits.org/) for messages, and +open your pull request against `main`. Found a bug or have an idea? Open an +[issue](https://github.com/opengeos/GeoLibre/issues). diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..00078f5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,101 @@ +# Run the build stage on the builder's native platform: the output is +# arch-independent static files, so emulating arm64 with QEMU here only +# slows down multi-arch builds without changing the result. +# ($BUILDPLATFORM is a Docker-provided automatic ARG, set by BuildKit.) +FROM --platform=$BUILDPLATFORM node:22-alpine AS build + +WORKDIR /app + +# Copy every workspace member's package.json before npm ci so the install +# layer is cached. Adding a new package under apps/ or packages/ requires +# adding its package.json here, or npm ci fails with a missing workspace. +COPY package.json package-lock.json ./ +COPY apps/geolibre-desktop/package.json apps/geolibre-desktop/package.json +COPY packages/core/package.json packages/core/package.json +COPY packages/map/package.json packages/map/package.json +COPY packages/plugins/package.json packages/plugins/package.json +COPY packages/processing/package.json packages/processing/package.json +COPY packages/ui/package.json packages/ui/package.json + +RUN npm ci + +COPY . . + +ARG GEOLIBRE_APP_BASE=/ +ARG VITE_GEE_OAUTH_CLIENT_ID= +ARG VITE_MAPILLARY_ACCESS_TOKEN= +ENV GEOLIBRE_APP_BASE=${GEOLIBRE_APP_BASE} +ENV VITE_GEE_OAUTH_CLIENT_ID=${VITE_GEE_OAUTH_CLIENT_ID} +ENV VITE_MAPILLARY_ACCESS_TOKEN=${VITE_MAPILLARY_ACCESS_TOKEN} + +RUN npm run build + +# Runtime image bundles the static web app (served by nginx) and the optional +# Python conversion/Whitebox sidecar (uvicorn), reverse-proxied at /sidecar. +# A glibc base (not alpine/musl) is required for the prebuilt geo wheels +# (duckdb, rasterio/rio-cogeo, freestiler, whitebox-workflows). +FROM python:3.12-slim-bookworm AS runtime + +# TARGETARCH is provided by BuildKit (amd64 / arm64). +ARG TARGETARCH + +# libexpat1 is a runtime dependency of rasterio (pulled in by rio-cogeo) that +# the slim base image does not ship. openssl provides `openssl passwd` used by +# entrypoint.sh to hash the optional Basic Auth password. +RUN apt-get update \ + && apt-get install -y --no-install-recommends nginx libexpat1 openssl \ + && rm -rf /var/lib/apt/lists/* \ + && rm -f /etc/nginx/sites-enabled/default + +# Install the sidecar package plus the core conversion stack. duckdb and +# rio-cogeo (rasterio) publish linux/arm64 wheels, so Vector->GeoParquet, +# CSV->GeoParquet and Raster->COG work on both architectures. +COPY backend/geolibre_server /opt/geolibre_server +RUN pip install --no-cache-dir /opt/geolibre_server \ + && pip install --no-cache-dir "duckdb>=1.1.0" "rio-cogeo>=5.0.0" + +# freestiler (PMTiles) and whitebox-workflows publish no linux/arm64 wheels, so +# they are installed on amd64 only. On arm64 those tools report unavailable +# while the other conversions keep working. +RUN if [ "$TARGETARCH" = "amd64" ]; then \ + pip install --no-cache-dir "freestiler>=0.1.0" "whitebox-workflows>=2.0.2"; \ + else \ + echo "Skipping freestiler + whitebox-workflows on $TARGETARCH (no wheels)"; \ + fi + +# Point the sidecar at this interpreter so it skips the managed-runtime +# bootstrap and uses the prebaked packages. Confine conversion reads/writes to +# /data by default: the sidecar is reachable same-origin through the nginx +# proxy, so without this an arbitrary same-origin caller could read or +# overwrite container paths. Mount input files at /data (read-write for +# outputs); override GEOLIBRE_CONVERSION_ROOTS to widen or disable. +ENV GEOLIBRE_CONVERSION_PYTHON=/usr/local/bin/python \ + WBW_EXTERNAL_PYTHON=/usr/local/bin/python \ + GEOLIBRE_CONVERSION_ROOTS=/data +RUN mkdir -p /data + +# WARNING: docker/nginx.conf's CSP allows http://localhost:* / http://127.0.0.1:* +# (and ws:// equivalents) in connect-src for local-dev data sources (PMTiles/COGs +# from a dev server on another port). This image is intended for local/single-user +# use; on a public host those allowances let the served JS probe each visitor's +# loopback. Drop them from the CSP before publishing publicly. +# Ship nginx.conf as an immutable template (not loaded directly). entrypoint.sh +# renders it to /etc/nginx/conf.d/default.conf on every boot, substituting the +# per-launch sidecar token, so a container restart never keeps a stale token. +COPY docker/nginx.conf /etc/nginx/nginx.conf.template +COPY docker/entrypoint.sh /usr/local/bin/entrypoint.sh +RUN chmod +x /usr/local/bin/entrypoint.sh \ + # Default auth snippet (disabled). entrypoint.sh rewrites it at start based + # on GEOLIBRE_AUTH_USER/GEOLIBRE_AUTH_PASSWORD; baking a valid default keeps + # `nginx -t` and non-entrypoint invocations working. + && printf '# Basic Auth disabled (GEOLIBRE_AUTH_USER/GEOLIBRE_AUTH_PASSWORD not set).\n' > /etc/nginx/geolibre-auth.conf +COPY --from=build /app/apps/geolibre-desktop/dist /usr/share/nginx/html + +EXPOSE 80 + +# /healthz is exempt from the optional Basic Auth, so the check keeps passing +# when GEOLIBRE_AUTH_USER/GEOLIBRE_AUTH_PASSWORD are set. +HEALTHCHECK --interval=30s --timeout=5s --start-period=5s --retries=3 \ + CMD python -c "import urllib.request,sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1/healthz', timeout=4).status==200 else 1)" || exit 1 + +CMD ["/usr/local/bin/entrypoint.sh"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..8e06999 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Qiusheng Wu + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..750de0c --- /dev/null +++ b/README.md @@ -0,0 +1,647 @@ +# GeoLibre + +[![Launch GeoLibre Web](https://img.shields.io/badge/Launch-GeoLibre%20Web-green.svg)](https://web.geolibre.app/?url=https://share.geolibre.app/giswqs/3d-tiles.geolibre.json) +[![GeoLibre shared project](https://img.shields.io/badge/GeoLibre-share-green.svg)](https://share.geolibre.app) +[![GeoLibre plugins](https://img.shields.io/badge/GeoLibre-plugins-green.svg)](https://plugins.geolibre.app) +[![image](https://img.shields.io/pypi/v/geolibre.svg)](https://pypi.python.org/pypi/geolibre) +[![image](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/opengeos/GeoLibre/blob/main/python/examples/getting-started.ipynb) +[![image](https://img.shields.io/conda/vn/conda-forge/geolibre.svg)](https://anaconda.org/conda-forge/geolibre) +[![Conda Recipe](https://img.shields.io/badge/recipe-geolibre-green.svg)](https://github.com/conda-forge/geolibre-feedstock) +[![Open in CodeSandbox](https://img.shields.io/badge/Open%20in-CodeSandbox-blue?logo=codesandbox)](https://codesandbox.io/p/github/opengeos/geolibre) +[![Open in StackBlitz](https://img.shields.io/badge/Open%20in-StackBlitz-blue?logo=stackblitz)](https://stackblitz.com/github/opengeos/geolibre) +[![Microsoft Store](https://img.shields.io/badge/Microsoft%20Store-GeoLibre-0078D4?logo=windows)](https://apps.microsoft.com/detail/9nwt67rv531x) +[![AUR version](https://img.shields.io/aur/version/geolibre-bin?logo=archlinux&label=AUR)](https://aur.archlinux.org/packages/geolibre-bin) +[![FlatPark](https://img.shields.io/badge/FlatPark-GeoLibre-4A90D9?logo=flatpak)](https://flatpark.org/apps/app.geolibre.GeoLibre/) +[![image](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20785400.svg)](https://doi.org/10.5281/zenodo.20785400) + + +A free and open-source, lightweight, cloud-native GIS platform for visualizing, exploring, and analyzing geospatial data. It runs everywhere you do, in the web browser, on the desktop, on mobile, and inside Jupyter notebooks, all while keeping your data local and private. + +GeoLibre is built with **Tauri v2**, **React**, **TypeScript**, **MapLibre GL JS**, **DuckDB-WASM Spatial**, and **deck.gl**. The same workspace runs as a native desktop app, a native Android app, in any modern web browser, and adapts responsively to mobile and small screens. + +[![GeoLibre demo showing 3D Tiles rendered on a MapLibre map](https://files.opengeos.org/GeoLibre-demo.webp)](https://web.geolibre.app/?url=https://share.geolibre.app/giswqs/3d-tiles.geolibre.json) + +**Video tutorials:** + +- [GeoLibre 1.0: A Free, Open-Source Cloud-Native GIS That Runs Anywhere (Browser, Desktop & Jupyter)](https://youtu.be/87Cm0QagtxI) +- [Geoprocessing in the Browser: 700+ Free GIS Tools in GeoLibre, Zero Install](https://youtu.be/W32bIQO_nG8) + +## Features (v2.0) + +- Runs across desktop (Tauri), web (browser), native Android (Tauri v2 mobile), and mobile or small screens, with a responsive, touch-friendly layout that adapts menus, dialogs, and panels (on phones the Layers/Style panels overlay the map as slide-over sheets), plus per-panel visibility through Layout settings +- MapLibre map workspace with OpenFreeMap, Protomaps, EOX Sentinel-2 cloudless, and Openbasiskaart basemaps, planetary basemaps for Mars and the Moon (OpenPlanetaryMap) plus Mercury, Venus, the Galilean moons (Io, Europa, Ganymede, Callisto), Titan, Pluto, and Charon (USGS Astrogeology, reprojected to Web Mercator by the tiles Worker) with a per-project ellipsoid whose radius drives distance, area, and scale measurements and a planet switcher in the Layers panel, stacking of multiple raster basemaps, blank background support, double-click to swap the core basemap from the layer panel, a right-click context menu for reading coordinates and quick actions, a Gridlines coordinate-grid overlay with edge labels and a UTM easting/northing grid mode, and toggleable navigation, fullscreen, geolocation, globe, terrain, scale, attribution, and logo controls, plus a View menu with viewport history navigation, a reset pitch and bearing control, a distinct north arrow, and View in Google Maps and View in Google Earth actions, and a double-click terrain control for setting vertical exaggeration +- Multi-map grid that splits the workspace into a grid of synchronized map views, so you can compare basemaps, layers, or time steps side by side, with any pane switchable to an optional CesiumJS 3D globe (camera-synced with the 2D maps; requires a Cesium Ion token — see [Environment variables](#environment-variables)) +- Load local vector layers supported by DuckDB-WASM Spatial, including common formats such as GeoJSON, GeoParquet, GeoPackage, Shapefile, FlatGeobuf, KML/KMZ (honoring embedded symbology, rendering GroundOverlay images as map overlays that animate through the Time Slider when time-tagged, and displaying embedded Collada `.dae` 3D models), GML, delimited text (including CSV without coordinates loaded as a standalone attribute table), GPX, and OpenStreetMap PBF extracts (parsed in-browser with osmix) +- Reproject vector layers to EPSG:4326 on load, render vector layers that carry Z coordinates in true 3D rather than flattening them onto the ground plane, and split dragged GPX files into named waypoint, track, and route layers +- Large local vector layers render through client-side vector tiling, with a warning before loading very large files +- Add Data menu for XYZ tiles, WMS and WFS (with layers and feature types discovered from the service's GetCapabilities so you pick from a populated dropdown), GeoJSON URLs, GeoRSS feeds (URL or file), vector tiles (including OGC API - Tiles vector tile services), COG and GeoTIFF rasters, Cloud-Optimized NetCDF/HDF (via kerchunk references) plus local HDF5 and NetCDF-4 files, MBTiles, ArcGIS FeatureServer and VectorTileServer layers, PMTiles, Zarr, LiDAR, 3D Tiles (including authenticated tilesets via custom request headers), ArcGIS I3S scene layers (Integrated Mesh and 3D Object layers rendered on deck.gl), CAD drawings (DXF/DWG) with a drawing-layer picker and CRS selector, Gaussian splats, glTF/GLB 3D models placed at coordinates, georeferenced video overlays, and geotagged photos imported as a point layer from their EXIF GPS (with manual placement and drag for photos lacking coordinates), with a fully internationalized dialog, comma decimal support, drag-and-dropped CSV coordinate files, sample-data dropdowns on every upstream-backed panel for loading ready-made example datasets, and a saved service library for storing and re-adding frequently used web-service endpoints +- Deck.gl Layer builder for composing deck.gl overlays from uploaded files or remote URLs +- Cloud data integrations through the Planetary Computer and Earth Engine panels, the Overture Maps plugin, and federal Web Services plugins +- Manual and automatic refresh for WFS, GeoJSON URL, and Add Vector Layer URL layers +- Layer panel for visibility, opacity, reordering, rename, zoom-to-layer, identify, labels, open attribute table, export, and remove actions, with collapsible layer groups/folders for organizing the layer stack and a Search places box in the footer for geocoding to a location without leaving the panel +- Live style panel with single, categorized, graduated, expression, and rule-based (filter-driven) symbology (fill, stroke, opacity, circle radius), proportional symbols, fill patterns, a built-in marker library, plus point heatmap and clustering renderers — all including for Add Vector Layer point layers, plus an inline color ramp picker that previews each colormap's gradient on the trigger and beside every option, and a transparent (no fill / no outline) option in the color picker, with vector layer symbology that imports and exports as OGC SLD, QGIS QML, and Mapbox GL style JSON so styles round-trip between GeoLibre, QGIS, and the Mapbox/MapLibre ecosystem +- Label engine for labeling vector features by any attribute, with ArcGIS-style placement and styling controls (anchor, X/Y offset, rotation, wrap width, letter case), a Duplicate labels option, and unique/concatenate modes that collapse points stacked at the same coordinate into a single deduplicated label +- Attribute table with filtering, sorting, resize controls, feature highlighting with Ctrl- and Shift-click multi-row selection, optional zoom to selected features, add-field and field-calculator tools (including geometry length and area calculation), a Charts panel (histogram, scatter, bar, line, box), a field statistics summary panel, column management (rename, delete, hide/show, reorder) with a column explorer for finding and toggling fields in wide tables, virtualized rows for large layers, and export to GeoJSON/GeoParquet/Shapefile/GeoPackage/CSV +- SQL Workspace for running DuckDB Spatial SQL against loaded layers, local files, and remote URLs, docked as a resizable panel beside the map with editor autocomplete for tables, columns, and SQL keywords, sample queries, query history, and adding results to the map or exporting them, plus an in-browser PostGIS SQL engine via PGlite and an Apache Sedona spatial SQL engine +- Multiple DuckDB SQL query-result layers with identify, selection, and attribute table support +- Controls menu with Measure, Bookmark, Minimap, View State, and Map annotation tools (draw text, arrows, and highlights on the map, saved with the project), a Search panel, persistent mode banners for the Directions and Reverse Geocode tools, a Camera Tour recorder that captures an animated keyframe tour to video (with per-keyframe recapture, per-keyframe hold and transition duration controls, and saving/loading a named tour setup as JSON), a Dashboard panel of configurable chart widgets that summarize the loaded layers, and a Print menu with a print layout composer (user-editable legend, explicit map-scale input, title block with editable title and footer, page-size controls, a custom print extent, and Copy to Clipboard) that exports the map to PNG or PDF +- Weather menu with live cloud and precipitation radar overlays (RainViewer), a Clouds overlay in the Controls menu, and a Google Earth-style sun position simulation that lights the scene for a given date and time +- Bookmarks that capture the active layers alongside the camera, organized into folders, with selectable export, a resizable and reorderable panel, and a save-as name prompt +- Field Collection tool for capturing point, line, and polygon observations with a per-layer custom form (text/number/date/choice fields and an optional photo), placed by device GPS or by tapping the map, written to a GeoJSON layer that flows into the attribute table, export, and offline use +- Story map builder that composes its chapters directly on the live map, with a presenter view, dedicated start and closing slides, an optional hide-itinerary toggle, a printable PDF handout generator (with subtitle and byline fields), and standalone HTML export +- Real-time multi-user collaboration (MVP; requires the `VITE_GEOLIBRE_COLLAB_URL` build variable — see [docs/collaboration.md](docs/collaboration.md)) so several people can edit the same project together, with per-participant permissions, an in-app chat panel, and an on-canvas session-status badge and roster (live dot, connected-participant count, and an expandable client list) while a session is active +- Natural-language GIS assistant that turns plain-English requests into auditable, undoable GeoLibre operations (Spatial SQL, symbology, add/remove data, and map control), provider-pluggable with your own API key (also read from OS environment variables) and a dedicated AI Providers settings section with per-feature provider dropdowns +- In-app Python Console plus a Python automation API for scripting the app +- Notebook panel docked beside the map for running Jupyter against the live map: the web build embeds a self-hosted JupyterLite site (in-browser Pyodide kernel) and the desktop build launches a uv-managed JupyterLab server, with notebook cells driving the map through an auto-loaded `geolibre` client. See [Notebook Panel](docs/notebook.md) +- Command palette (`Ctrl`/`Cmd` + `K`) that searches and runs menu and toolbar actions across Add Data, Processing, Controls, Plugins, and Help, global keyboard shortcuts for New/Open/Save/Save As and Google Earth-style camera resets (`N` north up, `U` top-down, `R` reset view), and a `?` shortcuts cheat sheet +- Conversion menu for Vector to GeoParquet/FlatGeobuf/PMTiles, a generic Vector to Vector converter that translates between any supported vector formats by file extension, CSV to GeoParquet, and Raster to COG; GeoParquet and CSV conversions run in the browser with DuckDB-WASM, while FlatGeobuf, PMTiles, and COG require the optional Python sidecar +- Whitebox toolbox that runs entirely in the browser through a WebAssembly runtime with raster I/O (no Python sidecar required), surfacing both Whitebox tools and GeoLibre's own WASM raster tools, browsable by category directly in the Processing menu with nested subcategory submenus and an offline-bundled tool catalog, with batch tools run against a selected input directory +- Vector menu with common geometry and analysis tools (buffer, centroids, convex hull, dissolve, bounding box, simplify, clip, intersection, difference, union, spatial join, attribute join, select by value, select by location, movement, space-time, and cell coverage) that run in the browser with Turf.js, an optional GeoPandas sidecar engine for every tool, and an in-browser GeoPandas engine via Pyodide (no server, same results as the sidecar) +- Raster menu with common raster tools (hillshade, slope, aspect, reproject, resample, clip by extent, clip by mask layer, polygonize, contour, zonal statistics, raster calculator, reclassify, mosaic, focal statistics) backed by a rasterio Python sidecar, with a client-side fallback so core tools also run in the browser when no sidecar is available, plus in-browser extraction of COG, WMS, and XYZ bounding-box subsets and a normalized-difference index builder for any HTTP COG +- Spectral Index toolbox (NDVI, GNDVI, NDWI, NDMI, NDBI, NBR, EVI, SAVI) with Sentinel-2, Landsat 8-9, NAIP, and custom band layouts, evaluated client-side with geotiff.js or on the rasterio sidecar +- Spatial Statistics toolbox and a Processing batch runner with model/pipeline chaining to run a sequence of tools as one job +- Raster Georeferencer (Processing → Georeferencing) that pins a non-georeferenced image to the map with ground control points using a least-squares affine fit, reporting per-GCP and RMS residuals +- Single-band pseudocolor with classification, reversed and custom color ramps, the full colormap list shown as inline gradient swatches in the Color ramp picker, a Legend populated automatically from a paletted raster's embedded color table, and RGB band combination for styling raster layers, plus COG pixel-value inspection from the Identify icon +- Network analysis tools for isochrones, service areas, origin–destination (OD) cost matrices, and sequential routes (directions) through an ordered set of waypoints +- Geocoding tools for forward, batch, and reverse geocoding through a multi-provider abstraction +- AI Segmentation (SamGeo) that turns imagery into vector features with [segment-geospatial](https://github.com/opengeos/segment-geospatial) and Meta's SAM 3 — text prompts ("trees", "buildings") or automatic segmentation, proxied to a separate `samgeo-api` model server (GPU recommended) +- H3 tools to create hexagonal grids over an extent and bin point layers into H3 cells +- Undo/redo for layer and style operations +- Drag and drop vector and GeoTIFF/COG raster files onto the map to add them as layers +- Project menu to create, open, save, and Save As `.geolibre.json` projects, export a project to a single standalone interactive HTML file that runs offline with no server, and a project gallery for browsing and opening shared projects with one click +- Desktop diagnostics panel (capturing native Tauri HTTP requests in the network log and classifying failed `fetch()` errors), a guided update workflow with a startup update check and update preferences, and MSIX packaging support, with macOS installers signed with an Apple Developer ID certificate and notarized by Apple so they open without a Gatekeeper workaround, plus Windows Package Manager (winget) distribution as `OpenGeos.GeoLibre` and a Windows portable zip build that runs without installation +- Customizable UI profiles that tailor which menus, panels, and data sources are visible, so a deployment can present a focused subset of the app to its users. See [UI Profiles](docs/ui-profiles.md) +- Plugin system with basemap, layer control, MapLibre components, swipe, street view, Mapillary coverage and street-level image viewer, Historical Imagery, Elevation Profile, Overture Maps, USGS LiDAR, GeoAgent, and GeoEditor integrations (the GeoEditor can pull the vector features currently visible in the map view into the editor for editing without re-importing the source, and write edits back to their origin, including GeoPackage and GeoJSON files and PostGIS database tables), including configurable control positions and external plugin manifests; external plugins can render on the host's shared deck.gl instance via `app.getDeckGL()`, use the maplibre-gl-raster stack and the map projection control, register native raster and tile layers, register first-class right-sidebar panels, toolbar menus, and floating panels through the plugin UI host API (including a shared-rail replace-style dock mode), and place their toolbar menus after the Help menu +- Time Slider plugin for animating time series raster and vector data, including binding existing vector layers already on the map to the timeline and a pixel time-series chart that plots a sampled pixel's value across a raster stack +- Atmosphere Effects plugin that renders a deep-space backdrop, parallax starfield, comets, and an atmospheric halo around the globe at low zoom (technique adapted from [Leonel Dias](https://leoneljdias.github.io/posts/globe-atmosphere-halo-comets/)), with a Spinning Globe panel and customizable atmosphere halo and deep-space colors +- Directions plugin for interactive routing via [maplibre-gl-directions](https://github.com/maplibre/maplibre-gl-directions): click the map to add waypoints, drag to reposition, and click a waypoint to remove it (uses the public OSRM demo server, driving only) +- Install external plugins from an uploaded zip on both desktop and web, plus external plugin zip loading from the app data plugins directory and local development plugin directories, with the Manage Plugins list sorted alphabetically +- Bundled drop-in plugins under `public/plugins//` that bake into both the web and desktop builds and load automatically with no manifest URL +- Browser deployment with Docker, embed-friendly URL parameters (including `?url=` project deep links that skip the welcome wizard and a `?welcome=0` param to opt out of onboarding), and a `maponly` chrome-free mode +- Native Android app built from the same codebase with Tauri v2 mobile, producing signed, per-architecture APKs (~40 MB) through a GitHub Actions workflow; tools that depend on a local desktop process (Whitebox, Raster, Conversion, AI Segmentation, PostgreSQL/Martin) are hidden on mobile so nothing is shown that cannot run. See [Android](docs/android.md) +- Installable, offline-capable Progressive Web App (PWA) build, plus a **Download Offline Area** tool that pre-caches the current map view's basemap tiles, and service-worker caching of the CDN-loaded Pyodide and PGlite/PostGIS engines so browser SQL and Python keep working offline after first use +- Internationalization framework with react-i18next and 13 complete per-build translation catalogs, plus a `?locale`/`?lang` query parameter to set the embed language +- Accessibility pass with axe-checked screens, keyboard navigation, and screen-reader labels +- App-wide, section, and plugin React error boundaries that contain failures and keep the rest of the workspace usable +- Python package (`geolibre`) that embeds the full app in Jupyter notebooks as an [anywidget](https://anywidget.dev), with an expanded leafmap-style API (local raster, marker/cluster, and choropleth layers; `split_map`, `add_legend`, and `add_colorbar` helpers; typed read-back of selected/drawn features; and `to_html` export) and two-way project sync +- Optional Python FastAPI sidecar for heavier processing workflows + +## Prerequisites + +- **Node.js** 22+ +- **Rust** toolchain ([rustup](https://rustup.rs/)) for Tauri desktop builds +- Linux: `webkit2gtk`, `libayatana-appindicator` (see [Tauri prerequisites](https://v2.tauri.app/start/prerequisites/)) + +## Install + +Prebuilt desktop installers for Linux, Windows, and macOS are published on the +[Releases](https://github.com/opengeos/GeoLibre/releases) page. On Windows you +can install the signed, auto-updating build from the +[Microsoft Store](https://apps.microsoft.com/detail/9nwt67rv531x), or the +unsigned GitHub Release build via `winget install OpenGeos.GeoLibre`. On macOS +you can install and update with Homebrew — GeoLibre is in the official +[Homebrew Cask](https://github.com/Homebrew/homebrew-cask/blob/main/Casks/g/geolibre.rb) +repository, so no tap is required: + +```bash +brew install --cask geolibre +``` + +The macOS app is signed with an Apple Developer ID certificate and notarized by +Apple, so it launches normally with no quarantine workaround. See +[Downloads](docs/downloads.md) for details and the manual install steps. + +To build from source instead: + +```bash +git clone https://github.com/opengeos/GeoLibre.git +cd GeoLibre +npm install +``` + +Bun users can run `bun install`. The root `trustedDependencies` list allows the known install scripts for `core-js`, `@google/genai`, and `protobufjs`. + +## Update + +To update an existing source checkout to the latest version, pull the changes, reinstall dependencies (in case `package.json` changed), and rebuild: + +```bash +cd /path/to/GeoLibre # your GeoLibre checkout +git pull origin main +npm install # or: bun install +``` + +If you run a production build, rebuild afterwards with `npm run build` (web) or `npm run tauri:build` (desktop). If you work from the dev servers (`npm run dev` or `npm run tauri:dev`), the `git pull` and `npm install` above are enough — just restart the dev server to pick up the changes. + +## Run (web dev, map in browser) + +```bash +npm run dev +``` + +Open http://localhost:5173. The map and browser vector import support local vector files that DuckDB-WASM Spatial can read, including common formats such as GeoJSON, GeoParquet, GeoPackage, Shapefile, FlatGeobuf, KML/KMZ, and GML, with direct handling for GeoJSON, zipped Shapefiles, and KMZ archives. You can choose files from Add Vector Layer or drag them onto the app. GeoTIFF/COG rasters can also be dragged onto the map to add them as raster layers. Desktop filesystem dialogs, local MBTiles, and local raster file reads require Tauri. + +## Run with Docker + +Build and run the browser version of GeoLibre: + +```bash +docker build -t geolibre . +docker run --rm -p 8080:80 geolibre +``` + +Open http://localhost:8080. The Docker image serves the production Vite build with nginx. Desktop-only features such as Tauri filesystem dialogs, local MBTiles, local raster file reads, and project save/open require the desktop app. + +### Bundled conversion sidecar + +The image also bundles the Python conversion/Whitebox sidecar (uvicorn) and +reverse-proxies it at `/sidecar`, so the browser reaches it same-origin with no +CORS or separate process to manage. `/conversion/status` is reachable at +`http://localhost:8080/sidecar/conversion/status`. + +- **Vector → GeoParquet** and **CSV → GeoParquet** run in the browser with + DuckDB-WASM and need no sidecar. +- **Vector → FlatGeobuf**, **Vector → PMTiles**, and **Raster → COG** use the + sidecar. These read a file **path on the sidecar's filesystem**, so from a + pure browser they currently work for files mounted into the container (a + browser cannot hand the container an absolute path); upload-based input is a + planned follow-up. The desktop app passes real local paths, so all + conversions work there. +- **PMTiles** and **Whitebox** are **amd64-only** in the container — + `freestiler` and `whitebox-workflows` publish no linux/arm64 wheels. On arm64 + the other conversions still work; those two report unavailable. + +Because the sidecar is reachable same-origin, conversion reads/writes are +confined to `GEOLIBRE_CONVERSION_ROOTS` (default `/data` in the image). Mount +your files there: + +```bash +docker run --rm -p 8080:80 -v "$PWD/data:/data" geolibre +``` + +Set `GEOLIBRE_DISABLE_SIDECAR=1` to run nginx only (the original web-only +behavior): + +```bash +docker run --rm -p 8080:80 -e GEOLIBRE_DISABLE_SIDECAR=1 geolibre +``` + +### Password protection (optional) + +Set `GEOLIBRE_AUTH_USER` and `GEOLIBRE_AUTH_PASSWORD` to put the whole +container (the app and the `/sidecar` API) behind HTTP Basic Auth: + +```bash +docker run --rm -p 8080:80 \ + -e GEOLIBRE_AUTH_USER=admin \ + -e GEOLIBRE_AUTH_PASSWORD='change-me' \ + geolibre +``` + +The browser prompts for the credentials on first visit. `/healthz` stays +unauthenticated so the container health check keeps working. When the +variables are unset (the default), no authentication is applied. + +As with any Docker env var, a password passed with `-e` lands in your shell +history and is readable on the host via `docker inspect`. Beyond quick local +testing, prefer `--env-file` with a permission-restricted file, or a secrets +manager. + +This is a single shared credential, not per-user accounts. Basic Auth sends +credentials with every request, so on anything beyond a trusted local network +put a TLS-terminating reverse proxy (Caddy, Traefik, nginx) in front of the +container. For multi-user or SSO needs, use an auth proxy such as +`oauth2-proxy` or Authelia in front of the unmodified image instead. Also see +the note in `docker/nginx.conf` about dropping the `localhost` CSP allowances +before exposing the image publicly. + +The published image is available from GitHub Container Registry: + +```bash +docker pull ghcr.io/opengeos/geolibre:latest +docker run --rm -p 8080:80 ghcr.io/opengeos/geolibre:latest +``` + +For deployments under a URL subpath, pass `GEOLIBRE_APP_BASE` at build time: + +```bash +docker build --build-arg GEOLIBRE_APP_BASE=/geolibre/ -t geolibre . +``` + +The container always serves the app from its root path. The build argument only sets the URL prefix that the app expects, so subpath deployments also require a reverse proxy in front of the container that strips the prefix before forwarding requests (for example, nginx `proxy_pass http://geolibre/;` with a trailing slash). + +## SQL Workspace + +The SQL Workspace runs DuckDB SQL (with the Spatial extension loaded, so `ST_*` functions are available) directly in the browser against your loaded layers and remote data. Open it from the Processing menu. + +- **Query loaded layers.** Every vector layer with in-memory features is exposed as a table; the queryable table names are listed at the top of the dialog. +- **Read files and URLs.** Use `read_parquet()`, `read_csv_auto()`, `read_json_auto()`, or `ST_Read()`. A bare URL or path after `FROM`/`JOIN` (for example `SELECT * FROM https://host/data.parquet`) is auto-wrapped in the matching reader. Remote files are streamed over HTTP range requests, so large datasets are not downloaded in full. +- **Sample queries.** A dropdown of ready-to-run examples (attribute-only, aggregate, and spatial queries) against a public sample dataset, plus a per-layer "sample query for layer" dropdown. +- **Query history.** Recently run queries are saved (in `localStorage`) and can be reloaded from the History dropdown. +- **Results and export.** Results show in a grid (capped for display; the full result is kept for export). When a query returns a geometry column, you can add the result to the map as a new layer (with an optional custom **layer name**) or export it as CSV or GeoParquet. + +```sql +SELECT NAME, CONTINENT, POP_EST, geom +FROM https://data.source.coop/giswqs/opengeos/countries.parquet +WHERE POP_EST > 50000000 +ORDER BY POP_EST DESC; +``` + +Only a single statement is supported per run. Cloud object-store URLs (`s3://`, `gs://`, `az://`) are transparently rewritten to their public HTTPS equivalents, so they work for anonymous / public datasets. + +## Vector tools + +The **Processing → Vector** menu opens a single Vector tools dialog with common geometry operations that run against your loaded GeoJSON layers. Pick a tool, choose the input layer (and an overlay layer for the two-layer tools), set the parameters, and the result is added to the map as a new layer. + +- **Geometry tools.** **Buffer** (by a distance in kilometers, meters, or miles), **Centroids** (one centroid point per feature), **Convex hull** (a single polygon enclosing all features), **Dissolve** (merge polygons, optionally grouped by an attribute field), **Bounding box** (the rectangular envelope of all features), **Simplify** (Douglas-Peucker vertex reduction), **Smooth** (spline-based line/polygon smoothing), **Regular grid** (a point or polygon grid over an extent), and **Voronoi / Delaunay** (Voronoi polygons or a Delaunay triangulation from a point layer). +- **Overlay tools.** **Clip** (clip the input to an overlay layer, keeping input attributes), **Intersection**, **Difference**, and **Union** between two polygon layers. +- **Join tools.** **Spatial join** (attach a join layer's attributes to each input feature by a spatial relationship — intersects, within, or contains — with an inner or left join, for any geometry type) and **Attribute join** (attach a join table's attributes by a matching key field, no geometry — e.g. join census stats to boundary polygons — choosing which fields to bring over, with an inner or left join). +- **Select tools.** **Select by value** (extract features whose attribute matches a condition — =, ≠, >, ≥, <, ≤, contains, starts with, is empty/not empty) and **Select by location** (extract features by their spatial relationship to a second layer — intersects, within, contains, or disjoint) into new layers. +- **Three engines.** Every tool runs fully in the browser with [Turf.js](https://turfjs.org/), so no sidecar is required. Every tool can also run on the optional GeoPandas sidecar for projection-aware results; when the sidecar is unavailable the dialog falls back to the client engine. A third **Python (Pyodide)** engine runs the same GeoPandas/Shapely code as the sidecar but **entirely in the browser** via [Pyodide](https://pyodide.org) — no server, so it works on the web build too. The first run lazily downloads the Python runtime from a CDN (override with `VITE_PYODIDE_INDEX_URL` to self-host for offline use); results match the sidecar because both share one `vector_ops.py`. + +To enable the sidecar engine, install the optional `vector` extra (it is not bundled by default to keep the sidecar small): + +```bash +# install the vector extras (GeoPandas, Shapely) +pip install -e "backend/geolibre_server[vector]" +# run it +geolibre-server # or: uvicorn geolibre_server.app.main:app --host 127.0.0.1 --port 8765 +``` + +## Raster tools + +The **Processing → Raster** menu opens a single Raster tools dialog with common raster operations. Because raster processing cannot run in the browser, these tools run on the Python sidecar (rasterio) with a file path in and a file path out: pick a tool, choose an input raster and an output file, set the parameters, and run the job. + +- **Terrain.** **Hillshade**, **Slope** (degrees or percent), and **Aspect** from an elevation model. +- **Reproject.** **Reproject** to a target CRS and **Resample** to a new pixel size, with selectable resampling (nearest, bilinear, cubic). +- **Clip.** **Clip by extent** (a bounding box in the raster's CRS) and **Clip by mask layer** (a GeoJSON mask, reprojected to the raster automatically). +- **Raster to vector.** **Polygonize** (vector polygons grouped by pixel value) and **Contour** (contour lines from an elevation model), written as GeoJSON. +- **Vector to raster.** **Interpolation (IDW / Kriging)** turns a point layer's numeric attribute into a continuous raster surface via inverse distance weighting or ordinary kriging. + +The tools share the conversion sidecar job runner. Install the optional `raster` extra (rasterio is also pulled in by the `conversion` extra): + +```bash +# install the raster extras (rasterio, numpy, contourpy) +pip install -e "backend/geolibre_server[raster]" +# run it +geolibre-server # or: uvicorn geolibre_server.app.main:app --host 127.0.0.1 --port 8765 +``` + +## AI segmentation + +The **Processing → AI Segmentation** dialog turns imagery into vector features with [segment-geospatial](https://github.com/opengeos/segment-geospatial) (SamGeo) and Meta's **SAM 3** model: choose a GeoTIFF, type a text prompt (e.g. "trees", "buildings", "water") or run automatic segmentation, and the resulting polygons are added as a new layer. + +The model stack does not run inside the sidecar. The sidecar exposes a thin `/ml` reverse-proxy in front of a separate `samgeo-api` server (the REST server shipped with `segment-geospatial`), which runs SAM 3 and returns GeoJSON. A CUDA GPU is strongly recommended. + +```bash +# install the model server (in an env with a working PyTorch build) +pip install "segment-geospatial[api,samgeo3]" +# install the sidecar's ml extra (just an HTTP client; models live in samgeo-api) +pip install -e "backend/geolibre_server[ml]" +``` + +`samgeo-api` is launched on demand when it's on the `PATH`, or point the sidecar at an existing server with `GEOLIBRE_ML_SAMGEO_URL=http://127.0.0.1:8000`. See [docs/user-guide/segmentation.md](docs/user-guide/segmentation.md) for details. Uses SAM 3. + +## Embed the demo + +The browser demo supports URL parameters for iframe-friendly layouts. + +Open a project by URL: + + + +Supported query parameters: + +| Parameter | Example | Description | +| ------------ | -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | +| `url` | `url=https://share.geolibre.app/giswqs/3d-tiles.geolibre.json` | Loads a `.geolibre.json` project from a public URL. | +| `layout` | `layout=compact` | Uses the compact embed layout with icon-only toolbar buttons and hidden project metadata. `embed` and `iframe` are aliases. | +| `toolbar` | `toolbar=icons` | Shows icon-only toolbar buttons without enabling the full compact layout. | +| `panels` | `panels=none` | Hides the Layers, Style, and Attribute table panels. `hidden`, `hide`, and `off` are aliases. | +| `hidePanels` | `hidePanels=true` | Alternative way to hide the Layers, Style, and Attribute table panels. | +| `maponly` | `maponly` | Hides all chrome (toolbar menu, Layers/Style/Attribute panels, and status bar), leaving only the map. The bare flag or any of `true`, `1`, `yes`, `on` enable it. | +| `theme` | `theme=dark` | Sets the initial color theme on load, overriding the OS preference. Accepts `dark` or `light`; the in-app toggle still works afterwards. | + +Use compact mode for narrow embeds. This shows icon-only toolbar buttons and hides project metadata: + +```text +https://web.geolibre.app/?url=https://share.geolibre.app/giswqs/3d-tiles.geolibre.json&layout=compact +``` + +Hide the Layers, Style, and Attribute table panels for map-focused embeds: + +```text +https://web.geolibre.app/?url=https://share.geolibre.app/giswqs/3d-tiles.geolibre.json&layout=compact&panels=none +``` + +Use `toolbar=icons` when you only want icon-only toolbar buttons. `panels=hidden`, `panels=hide`, `panels=off`, and `hidePanels=true` are accepted aliases for hiding panels. + +For a fully chrome-free, map-only embed, use `maponly`. It hides the toolbar menu, all panels, and the status bar: + +```text +https://web.geolibre.app/?url=https://share.geolibre.app/giswqs/3d-tiles.geolibre.json&maponly +``` + +## Python package (Jupyter) + +GeoLibre ships a Python package that embeds the **full** GeoLibre app (menus, +panels, processing tools) in a Jupyter notebook cell as an +[anywidget](https://anywidget.dev), with a leafmap-style API. State syncs both +ways through a single `.geolibre.json` project, so data you add from Python +appears in the UI, and edits you make in the UI are readable back from Python. + +```bash +pip install geolibre +``` + +Or with conda: + +```bash +conda install -c conda-forge geolibre +``` + +```python +from geolibre import Map + +m = Map(center=(-100, 40), zoom=4) +m.add_geojson("https://example.com/data.geojson", name="Data") +m.add_tile_layer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", name="OpenStreetMap") +m.add_cog("https://example.com/dem.tif", name="DEM") +m # the full GeoLibre UI renders in the cell +``` + +Read state edited in the UI, and round-trip projects: + +```python +m.to_project()["mapView"]["center"] # reflects the live UI view after panning +m.save_project("my-map.geolibre.json") +Map().load_project("my-map.geolibre.json") +``` + +The package source lives in [`python/`](python/), and the bundled web app is +built into the wheel by `npm run build:embed`. The interactive widget works in +local Jupyter, VS Code, Google Colab (its built-in port proxy is used +automatically), and JupyterHub / remote servers (through a Jupyter Server +extension bundled with the wheel and auto-enabled on install, so managed hubs +work without `jupyter-server-proxy`; pass `Map(server_proxy=True)` on non-Hub +remote setups). See the [Python package guide](docs/python.md) for the full API. + +## Environment variables + +The Street View plugin can use Google Street View and Mapillary imagery. Create `apps/geolibre-desktop/.env.local` and set one or both provider credentials: + +```env +VITE_GOOGLE_MAPS_API_KEY=your_google_maps_api_key +VITE_MAPILLARY_ACCESS_TOKEN=your_mapillary_access_token +``` + +For Google Street View, enable the Maps Embed API for the key in Google Cloud. For Mapillary, create an app in the Mapillary developer dashboard and use its client access token. + +The optional **Cesium 3D-globe view** — a split-pane globe rendered with [CesiumJS](https://cesium.com/platform/cesiumjs/) alongside the 2D MapLibre map — needs a [Cesium Ion](https://ion.cesium.com/) access token for its world imagery and terrain. Create a free Ion account, copy your default access token, and set it at build time: + +```env +CESIUM_TOKEN=your_cesium_ion_access_token +``` + +`CESIUM_TOKEN` (or the `VITE_`-prefixed `VITE_CESIUM_TOKEN`) is read by `vite.config.ts` and baked into the build. A user can **also set it at runtime** — with no rebuild — in the Settings dialog's **Environment Variables** section, which has a dedicated masked **Cesium Ion token** field. That token is stored locally on the device (in browser storage on the web build), **not** in the shared project file, and overrides the build-time value; it is how a web user brings their own Ion token. (A free-form `VITE_CESIUM_TOKEN` variable in the same section still works and takes precedence, as an override.) Without a token from any source, the 3D-globe toggle is hidden entirely (the 2D map is unaffected). Ion access tokens are designed to ship in client bundles. See [docs/architecture.md](docs/architecture.md#3d-globe-view-cesiumjs) for how the globe integrates. + +The optional **Python (Pyodide)** vector engine loads its runtime from the public jsDelivr CDN by default. To self-host it for offline or production use, point it at a mirrored copy of the Pyodide distribution: + +```env +VITE_PYODIDE_INDEX_URL=https://your-host/pyodide/v0.27.7/full/ +``` + +Similarly, the DuckDB Spatial extension is installed from DuckDB's remote +extension repository by default. To load it from a mirror instead (so +`INSTALL spatial` is skipped and the extension is loaded directly), set the full +path or URL to the extension file: + +```env +VITE_DUCKDB_SPATIAL_EXTENSION_PATH=https://your-host/duckdb/spatial.duckdb_extension.wasm +``` + +Both `VITE_PYODIDE_INDEX_URL` and `VITE_DUCKDB_SPATIAL_EXTENSION_PATH` can also be set at runtime through the Settings dialog's runtime environment variables (no rebuild required), so air-gapped or corporate deployments can point Pyodide and the DuckDB Spatial extension at internal mirrors without rebuilding. + +Restart `npm run dev` or `npm run tauri:dev` after changing these values. Vite only exposes variables with the `VITE_` prefix to the frontend. + +## Run (desktop) + +```bash +npm run tauri:dev +``` + +## Build + +```bash +npm run build +npm run tauri:build +``` + +The default desktop build keeps the Linux binary small and uses DuckDB-WASM for +DuckDB-backed browser features. To build a larger desktop binary with the native +`duckdb-rs` vector loader enabled, run: + +```bash +npm run tauri:build:native-duckdb +``` + +Where to find the output: + +- **Web build** — static files in `apps/geolibre-desktop/dist/`. Serve this directory with any static web server (or the Docker image above). +- **Desktop installers** — `apps/geolibre-desktop/src-tauri/target/release/bundle/`, with per-platform subfolders: `deb/`, `rpm/`, and `appimage/` on Linux; `msi/` and `nsis/` on Windows; `dmg/` and `macos/` on macOS. The unbundled executable is in `apps/geolibre-desktop/src-tauri/target/release/`. On Linux, `npm run tauri:build` builds `deb` and `rpm` by default; passing `--bundles` replaces that default selection rather than adding to it, so list every format you want, for example `npm run tauri:build -- --bundles deb,rpm,appimage` for all three. + +## Android + +GeoLibre builds as a native Android app from the same codebase via Tauri v2 +mobile. You need the Android SDK + NDK, a JDK (17 or 21), and the Rust Android +targets; see [docs/android.md](docs/android.md) for the full toolchain setup, +signing, and sideloading guide. Once set up: + +```bash +cd apps/geolibre-desktop +npx tauri android init # generate the Gradle project (once) +npm run tauri android dev # run on a connected device/emulator +npx tauri android build --apk --split-per-abi # release APKs, ~40 MB per ABI +``` + +Install the `arm64-v8a` APK on real phones. The CI workflow +(`.github/workflows/android.yml`) builds and signs per-ABI release APKs on each +published GitHub release (and on demand via the "Run workflow" button) and +uploads them as artifacts; set the `ANDROID_KEYSTORE_*` repository secrets +to sign with a real release key (otherwise a debug key is used for testable +builds). Heavy tools that need the Python sidecar or local helper processes are +hidden on mobile. + +## Quality checks + +Run the fast TypeScript unit tests: + +```bash +npm run test:frontend +``` + +Run the full local quality gate: + +```bash +npm run ci +``` + +## Optional Python sidecar + +```bash +cd backend/geolibre_server +python -m venv .venv && source .venv/bin/activate +pip install -e . +uvicorn geolibre_server.app.main:app --host 127.0.0.1 --port 8765 +``` + +### Conversion tools (Processing → Conversion) + +The **Processing → Conversion** menu (Vector → GeoParquet / FlatGeobuf, +CSV → GeoParquet, Vector → PMTiles, Raster → COG) talks to this sidecar at +`http://127.0.0.1:8765`. **Vector → GeoParquet** and **CSV → GeoParquet** also +run fully in the browser with DuckDB-WASM and need no sidecar; the others +require it. + +To use them from the **web** build, start the sidecar and serve the app from +`localhost:5173` (CORS is restricted to that origin and the Tauri origins): + +```bash +# install the conversion extras (DuckDB, rio-cogeo, freestiler) +pip install -e "backend/geolibre_server[conversion]" +# run it +geolibre-server # or: uvicorn geolibre_server.app.main:app --host 127.0.0.1 --port 8765 +``` + +The sidecar self-bootstraps a managed runtime on first use; set +`GEOLIBRE_CONVERSION_PYTHON=$(which python)` to reuse the current environment +instead. See [backend/geolibre_server/README.md](backend/geolibre_server/README.md) +for details. + +## Repository layout + +``` +apps/geolibre-desktop # Tauri + React app +packages/core # Types, store, project format +packages/map # MapLibre integration +packages/ui # Tailwind + shadcn/ui +packages/plugins # Plugin API +packages/processing # Algorithm registry +backend/geolibre_server # FastAPI sidecar +python/ # geolibre Python package (Jupyter anywidget) +docs/ # Architecture & API docs +``` + +## Add a plugin + +Built-in plugins live in `packages/plugins/src/plugins/` and are registered by the desktop app in `apps/geolibre-desktop/src/hooks/usePlugins.ts`. Map control plugins can expose a control position through `getMapControlPosition()` and `setMapControlPosition()` so the Plugins menu can move them between map corners. + +For external plugin development, start from the [GeoLibre plugin template](https://github.com/opengeos/geolibre-plugin-template). It includes a `plugin.json` manifest, a GeoLibre plugin wrapper entry point, and a `package:geolibre` script that creates a zip file for the desktop app data `plugins/` directory. During development, Settings → Manage Plugins can scan an additional local plugin directory, including an unpacked bundle folder such as the template's `geolibre-plugin/` directory, or a hosted `plugin.json` manifest URL. See the [Plugin API](docs/plugin-api.md) for the external plugin contract. + +To bake an external plugin into the build so it loads automatically — with no Settings entry and no manifest URL — drop its built folder into `apps/geolibre-desktop/public/plugins//` (the same `plugin.json` + `dist/` a manifest URL would serve). The `bundledPlugins()` Vite plugin discovers it at build time and the app loads it through the normal external-plugin path. The same folder serves both the web build and the desktop build (which ships the same frontend), so one drop-in covers both. Private plugin bundles are git-ignored under that folder and copied in at build/deploy time. See the [Plugin API](docs/plugin-api.md#bundled-plugins-baked-into-the-build) for details and the security model. + +1. Create a plugin file in `packages/plugins/src/plugins/`. + +```typescript +import type { GeoLibreAppAPI, GeoLibrePlugin } from "../types"; + +export const myPlugin: GeoLibrePlugin = { + id: "my-plugin", + name: "My Plugin", + version: "0.1.0", + activate: (app: GeoLibreAppAPI) => { + app.setBasemap("https://example.com/style.json"); + }, + deactivate: () => {}, +}; +``` + +2. Export it from `packages/plugins/src/index.ts`. + +```typescript +export { myPlugin } from "./plugins/my-plugin"; +``` + +3. Register it in `apps/geolibre-desktop/src/hooks/usePlugins.ts`. + +```typescript +import { myPlugin } from "@geolibre/plugins"; + +manager.registerAll([ + maplibreLayerControlPlugin, + maplibreGeoAgentPlugin, + maplibreGeoEditorPlugin, + myPlugin, +]); +``` + +Plugins can use the app API to change basemaps, add GeoJSON layers, or attach MapLibre controls. For a MapLibre control plugin, add the package dependency, import its CSS in `apps/geolibre-desktop/src/main.tsx`, then call `app.addMapControl(control, "top-left")` in `activate()` and `app.removeMapControl(control)` in `deactivate()`. + +Built-in MapLibre controls such as Navigation, Fullscreen, Geolocate, Globe, Terrain, Scale, Attribution, and Logo are toggled from the desktop app's Controls menu. The same menu also opens Search, a standalone place search panel backed by the Components plugin. Keep project-specific controls such as Layer Control and Components in the plugin menu when they use the plugin API or need plugin lifecycle behavior. + +The Components plugin wraps `maplibre-gl-components` controls and wires their layer events into the GeoLibre store. It provides Add Data shortcuts for FlatGeobuf, PMTiles, Zarr, LiDAR, and Gaussian splats, while raster COG and GeoTIFF layers can also be added through the standard Add Raster Layer dialog. + +If a third-party MapLibre control needs app-specific styling fixes, add scoped overrides in `apps/geolibre-desktop/src/index.css` instead of editing files in `node_modules`. Keep selectors limited to the plugin control class. For example, GeoEditor toolbar buttons need a local override because MapLibre's default control button CSS can override their flex centering: + +```css +.geo-editor-control .geo-editor-tool-button { + align-items: center; + display: flex !important; + justify-content: center; + line-height: 0; + padding: 0; +} + +.geo-editor-control .geo-editor-tool-button svg { + display: block; + flex: 0 0 auto; + margin: 0; +} +``` + +Run checks before submitting changes: + +```bash +npm run build +pre-commit run --all-files +``` + +## Documentation + +Full documentation, including the User Guide and Tutorials, is published at +**[geolibre.app](https://geolibre.app)**. + +- **User Guide** - a [feature-by-feature reference](https://geolibre.app/user-guide/interface/) for the interface, adding data, layers, styling, the attribute table, map controls, processing, the SQL Workspace, data integrations, plugins, settings, and embedding. +- **Tutorials** - [hands-on, end-to-end workflows](https://geolibre.app/tutorials/): your first map, cloud-native data, vector analysis, terrain analysis, spatial SQL, and sharing and embedding. +- **Reference** + - [Architecture](docs/architecture.md) + - [Android](docs/android.md) + - [Project format](docs/project-format.md) + - [Plugin API](docs/plugin-api.md) + - [UI Profiles](docs/ui-profiles.md) + - [Python package (Jupyter)](docs/python.md) + - [Roadmap](docs/roadmap.md) + - [How to Cite](docs/citation.md) + +## Acknowledgements + +GeoLibre is built on the free and open-source geospatial and web communities — including MapLibre GL JS, deck.gl, DuckDB-WASM Spatial, Turf.js, Tauri, React, and many more. See the full [Acknowledgements](https://geolibre.app/acknowledgements/) page for the complete list of projects and community contributors. + +- The **Atmosphere Effects** plugin (deep-space backdrop, parallax starfield, comets, and the globe atmosphere halo) adapts the technique and visual design from [Leonel Dias](https://leoneljdias.github.io/)'s article [*Globe atmosphere, halo, and comets*](https://leoneljdias.github.io/posts/globe-atmosphere-halo-comets/) — the layered Canvas 2D approach, the halo gradient and "screen" blend, the limb-sampling that keeps the halo aligned under pitch, and the starfield/comet parameters. +- **Community contributors** — thanks to [**Ryanphoenix**](https://github.com/Ryanphoenix) for many valued contributions, including issue reports, feedback, and improvements. +- **Beta testers** — thanks to [**René van der Velde**](https://github.com/renevandervelde) (Netherlands) for early testing, detailed bug reports, and feature requests. + +## Citation + +If you use GeoLibre in your work, please cite it. GeoLibre is archived on [Zenodo](https://zenodo.org/), which mints a DOI for every release. The concept DOI below always resolves to the latest version. + +[![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.20785400.svg)](https://doi.org/10.5281/zenodo.20785400) + +> Wu, Q. (2026). GeoLibre: A lightweight, cloud-native GIS platform for visualizing, exploring, and analyzing geospatial data. Zenodo. + +You can also use GitHub's **"Cite this repository"** button (which reads [`CITATION.cff`](CITATION.cff)) to copy a ready-made APA or BibTeX entry. See the [How to Cite](https://geolibre.app/citation/) page for more formats. + +## License + +MIT diff --git a/README.wehub.md b/README.wehub.md new file mode 100644 index 0000000..067aacb --- /dev/null +++ b/README.wehub.md @@ -0,0 +1,7 @@ +# WeHub 来源说明 + +- 原始项目:`opengeos/GeoLibre` +- 原始仓库:https://github.com/opengeos/GeoLibre +- 导入方式:上游默认分支的最新快照 +- 原作者、版权和许可证信息以原始仓库及本仓库 LICENSE 为准 +- 本文件仅用于记录来源,不代表 WeHub 是原项目作者 diff --git a/apps/geolibre-desktop/index.html b/apps/geolibre-desktop/index.html new file mode 100644 index 0000000..eb27a17 --- /dev/null +++ b/apps/geolibre-desktop/index.html @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + GeoLibre + + +
+ + + diff --git a/apps/geolibre-desktop/jupyterlite/jupyter-lite.json b/apps/geolibre-desktop/jupyterlite/jupyter-lite.json new file mode 100644 index 0000000..82436b8 --- /dev/null +++ b/apps/geolibre-desktop/jupyterlite/jupyter-lite.json @@ -0,0 +1,6 @@ +{ + "jupyter-lite-schema-version": 0, + "jupyter-config-data": { + "exposeAppInBrowser": true + } +} diff --git a/apps/geolibre-desktop/jupyterlite/jupyter_lite_config.json b/apps/geolibre-desktop/jupyterlite/jupyter_lite_config.json new file mode 100644 index 0000000..5cd345a --- /dev/null +++ b/apps/geolibre-desktop/jupyterlite/jupyter_lite_config.json @@ -0,0 +1,6 @@ +{ + "LiteBuildConfig": { + "apps": ["lab", "tree", "notebooks", "repl"], + "no_unused_shared_packages": true + } +} diff --git a/apps/geolibre-desktop/jupyterlite/requirements.txt b/apps/geolibre-desktop/jupyterlite/requirements.txt new file mode 100644 index 0000000..61827d5 --- /dev/null +++ b/apps/geolibre-desktop/jupyterlite/requirements.txt @@ -0,0 +1,12 @@ +# Build-time dependencies for the self-hosted JupyterLite site embedded in the +# GeoLibre web build (see scripts/build-jupyterlite.mjs). These are NOT runtime +# app dependencies — they are only needed by whoever runs `npm run +# build:jupyterlite` to regenerate apps/geolibre-desktop/public/jupyterlite/. +# +# The in-browser Python kernel itself ships as WASM via jupyterlite-pyodide-kernel +# and pulls Pyodide (numpy, pandas, …) at runtime from a CDN; geopandas/shapely +# install on demand with `%pip install ...` / piplite. +jupyterlite-core>=0.4,<0.6 +jupyterlite-pyodide-kernel>=0.4,<0.6 +jupyterlab>=4.2,<5 +notebook>=7.2,<8 diff --git a/apps/geolibre-desktop/package.json b/apps/geolibre-desktop/package.json new file mode 100644 index 0000000..e64591a --- /dev/null +++ b/apps/geolibre-desktop/package.json @@ -0,0 +1,110 @@ +{ + "name": "geolibre-desktop", + "version": "2.0.0", + "private": true, + "type": "module", + "scripts": { + "dev": "vite", + "predev": "node ../../scripts/build-jupyterlite.mjs --if-missing", + "prebuild": "node ../../scripts/build-jupyterlite.mjs", + "build": "tsc -b && vite build", + "preview": "vite preview", + "tauri": "tauri", + "tauri:dev": "tauri dev", + "tauri:build": "node ../../scripts/tauri-build.mjs", + "tauri:build:native-duckdb": "node ../../scripts/tauri-build.mjs --native-duckdb" + }, + "dependencies": { + "@anthropic-ai/sdk": "^0.110.0", + "@carbonplan/zarr-layer": "^0.6.0", + "@cereusdb/standard": "^0.2.0", + "@deck.gl/aggregation-layers": "9.3.6", + "@deck.gl/core": "^9.3.6", + "@deck.gl/geo-layers": "^9.3.6", + "@deck.gl/layers": "^9.3.6", + "@deck.gl/mapbox": "^9.3.6", + "@deck.gl/mesh-layers": "^9.3.6", + "@developmentseed/deck.gl-geotiff": "^0.7.0", + "@developmentseed/deck.gl-raster": "^0.7.0", + "@duckdb/duckdb-wasm": "1.33.1-dev57.0", + "@dvt3d/maplibre-three-plugin": "^1.6.0", + "@electric-sql/pglite": "^0.5.4", + "@electric-sql/pglite-postgis": "^0.2.4", + "@geolibre/core": "*", + "@geolibre/map": "*", + "@geolibre/plugins": "*", + "@geolibre/processing": "*", + "@geolibre/ui": "*", + "@geoman-io/maplibre-geoman-free": "^0.8.4", + "@google/genai": "^2.10.0", + "@mapbox/mapbox-gl-rtl-text": "^0.4.0", + "@modelcontextprotocol/sdk": "^1.25.2", + "@opentelemetry/api": "^1.9.0", + "@osmix/core": "^0.1.10", + "@osmix/geojson": "^0.0.15", + "@osmix/pbf": "^0.0.9", + "@strands-agents/sdk": "^1.7.0", + "@tanstack/react-virtual": "^3.14.5", + "@tauri-apps/api": "^2.11.1", + "@tauri-apps/plugin-dialog": "^2.7.1", + "@tauri-apps/plugin-fs": "^2.5.1", + "@tauri-apps/plugin-http": "^2.5.2", + "@tauri-apps/plugin-opener": "^2.5.4", + "apache-arrow": "^21.1.0", + "cesium": "^1.143.0", + "dompurify": "^3.4.10", + "exifr": "^7.1.3", + "fflate": "^0.8.3", + "gdal3.js": "^2.8.1", + "i18next": "^26.3.4", + "jspdf": "^4.2.1", + "mapillary-js": "^4.1.2", + "maplibre-gl": "^5.24.0", + "maplibre-gl-3d-tiles": "^0.5.4", + "maplibre-gl-basemap-control": "^0.12.0", + "maplibre-gl-components": "^0.25.4", + "maplibre-gl-duckdb": "^0.2.3", + "maplibre-gl-earth-engine": "^0.4.2", + "maplibre-gl-enviroatlas": "^0.1.1", + "maplibre-gl-esri-wayback": "^0.2.2", + "maplibre-gl-fema-wms": "^0.1.2", + "maplibre-gl-geo-editor": "^0.10.1", + "maplibre-gl-geoagent": "^0.5.4", + "maplibre-gl-lidar": "^0.16.2", + "maplibre-gl-nasa-earthdata": "^0.1.4", + "maplibre-gl-national-map": "^0.1.1", + "maplibre-gl-planetary-computer": "^0.3.0", + "maplibre-gl-raster": "^0.10.1", + "maplibre-gl-splat": "^0.2.8", + "maplibre-gl-streetview": "^0.7.0", + "maplibre-gl-swipe": "^0.10.1", + "maplibre-gl-time-slider": "^1.1.0", + "maplibre-gl-usgs-lidar": "^0.11.1", + "maplibre-gl-vector": "^0.9.2", + "openai": "^6.39.0", + "qrcode.react": "^4.2.0", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-i18next": "^17.0.8", + "shpjs": "^6.2.0", + "sql.js": "^1.14.0", + "three": "^0.185.1", + "zod": "^4.4.3" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4.3.2", + "@tauri-apps/cli": "^2.11.4", + "@types/geojson": "^7946.0.16", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@types/sql.js": "^1.4.9", + "@vitejs/plugin-react": "^6.0.3", + "esbuild": "^0.28.0", + "osmix": "^0.2.2", + "postcss": "^8.5.16", + "tailwindcss": "^4.3.0", + "typescript": "^6.0.3", + "vite": "^8.1.3", + "vite-plugin-pwa": "^1.3.0" + } +} diff --git a/apps/geolibre-desktop/postcss.config.js b/apps/geolibre-desktop/postcss.config.js new file mode 100644 index 0000000..c2ddf74 --- /dev/null +++ b/apps/geolibre-desktop/postcss.config.js @@ -0,0 +1,5 @@ +export default { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; diff --git a/apps/geolibre-desktop/public/apple-touch-icon.png b/apps/geolibre-desktop/public/apple-touch-icon.png new file mode 100644 index 0000000..3b48a6f Binary files /dev/null and b/apps/geolibre-desktop/public/apple-touch-icon.png differ diff --git a/apps/geolibre-desktop/public/favicon.ico b/apps/geolibre-desktop/public/favicon.ico new file mode 100644 index 0000000..d87b600 Binary files /dev/null and b/apps/geolibre-desktop/public/favicon.ico differ diff --git a/apps/geolibre-desktop/public/favicon.png b/apps/geolibre-desktop/public/favicon.png new file mode 100644 index 0000000..c1153ed Binary files /dev/null and b/apps/geolibre-desktop/public/favicon.png differ diff --git a/apps/geolibre-desktop/public/maskable-icon-512x512.png b/apps/geolibre-desktop/public/maskable-icon-512x512.png new file mode 100644 index 0000000..edfd394 Binary files /dev/null and b/apps/geolibre-desktop/public/maskable-icon-512x512.png differ diff --git a/apps/geolibre-desktop/public/plugins/.gitignore b/apps/geolibre-desktop/public/plugins/.gitignore new file mode 100644 index 0000000..c8cb122 --- /dev/null +++ b/apps/geolibre-desktop/public/plugins/.gitignore @@ -0,0 +1,6 @@ +# Plugins baked into the build are dropped here as public/plugins//. +# The plugin bundles themselves are not committed (they may be private and are +# copied in at build/deploy time); only this folder's convention docs are. +* +!.gitignore +!README.md diff --git a/apps/geolibre-desktop/public/plugins/README.md b/apps/geolibre-desktop/public/plugins/README.md new file mode 100644 index 0000000..60517c2 --- /dev/null +++ b/apps/geolibre-desktop/public/plugins/README.md @@ -0,0 +1,44 @@ +# Bundled plugins (drop-in folder) + +Drop a plugin here to **bake it into the GeoLibre build**. It loads +automatically on startup with no Settings entry and no manifest URL — on both +the **web** build and the **desktop** build (the desktop app ships the same +frontend, so one folder serves both). + +## Layout + +One folder per plugin, named by its plugin id, with a `plugin.json` at its root +(the exact same content a manifest URL would serve): + +```text +public/plugins/ + my-plugin/ + plugin.json # { "id", "name", "version", "entry", "style"? } + dist/ + index.js # the "entry" referenced by plugin.json + style.css # the optional "style" referenced by plugin.json +``` + +`entry` and `style` in `plugin.json` are resolved relative to the manifest, so +keep them inside the plugin's own folder. + +## How it works + +The `bundledPlugins()` Vite plugin +(`apps/geolibre-desktop/vite-plugins/bundled-plugins.ts`) scans this directory +at build and dev-server start and exposes the discovered manifest paths via the +`virtual:bundled-plugins` module. `usePlugins.ts` turns those into origin-absolute +URLs and loads them through the normal external-plugin path (fetch → blob import +→ register). Adding or removing a plugin is just adding or removing a folder — +no code changes. + +Discovery happens at **build time**, so a dev server or production build must be +(re)started after adding, updating, or removing a plugin folder. + +## Private plugins + +The bundles are **git-ignored** (see `.gitignore`) so private plugin code stays +out of this repo's history. Copy the plugin folder in at build/deploy time (for +example, in CI before `npm run build`, or with a plugin repo's own install +script). The discovery code is generic and committed; only the plugin payload +is excluded. diff --git a/apps/geolibre-desktop/public/pwa-192x192.png b/apps/geolibre-desktop/public/pwa-192x192.png new file mode 100644 index 0000000..a570783 Binary files /dev/null and b/apps/geolibre-desktop/public/pwa-192x192.png differ diff --git a/apps/geolibre-desktop/public/pwa-512x512.png b/apps/geolibre-desktop/public/pwa-512x512.png new file mode 100644 index 0000000..3da67b4 Binary files /dev/null and b/apps/geolibre-desktop/public/pwa-512x512.png differ diff --git a/apps/geolibre-desktop/public/pyodide/pyodide-worker.js b/apps/geolibre-desktop/public/pyodide/pyodide-worker.js new file mode 100644 index 0000000..986fbbb --- /dev/null +++ b/apps/geolibre-desktop/public/pyodide/pyodide-worker.js @@ -0,0 +1,103 @@ +/* + * GeoLibre Pyodide vector worker (classic Web Worker). + * + * Runs the GeoPandas/Shapely vector tools entirely in the browser. This is a + * plain JS file served from public/ (NOT bundled by Vite) on purpose: Pyodide + * 0.27's loader only supports a classic worker (importScripts) or the main + * thread — a module worker hits its "Cannot determine runtime environment" + * error. Loading the CDN UMD via importScripts also avoids bundling + * pyodide.mjs's dynamic node: imports. + * + * Protocol (postMessage): + * in: { type: "init", indexURL, vectorOpsSource } + * { type: "run", id, request } // request = {tool_id, geojson, overlay, parameters} + * out: { type: "progress", phase } + * { type: "ready" } + * { type: "result", id, geojson, messages } + * { type: "error", id, message } // id omitted for init failures + * + * The main thread (pyodide-vector-loader.ts) reads the Python source via a Vite + * ?raw import and hands it over in the init message, so the single + * source-of-truth backend module is what runs here. + */ + +let readyPromise = null; +let pyodide = null; + +async function initialize(indexURL, vectorOpsSource) { + self.postMessage({ type: "progress", phase: "Downloading Python runtime" }); + // The UMD build attaches loadPyodide to the worker global scope. + // Trust note: importScripts has no SRI, and Pyodide loads its own + // pyodide.asm.js/WASM from indexURL internally, so verifying this entry + // script alone would not secure the runtime. The default CDN therefore + // carries a trust assumption; self-host via VITE_PYODIDE_INDEX_URL to remove + // the external dependency (see docs/user-guide/processing.md). + self.importScripts(`${indexURL}pyodide.js`); + pyodide = await self.loadPyodide({ indexURL }); + + self.postMessage({ type: "progress", phase: "Loading GeoPandas" }); + // geopandas pulls shapely, pyproj, pandas, numpy, fiona transitively. + await pyodide.loadPackage("geopandas"); + + // Define run_vector_tool / run_vector_tool_json (the shared backend module). + pyodide.runPython(vectorOpsSource); + self.postMessage({ type: "ready" }); +} + +self.onmessage = async (event) => { + const data = event.data || {}; + + if (data.type === "init") { + readyPromise ??= initialize(data.indexURL, data.vectorOpsSource).catch( + (err) => { + // Reset so a later init can retry after a transient failure. + readyPromise = null; + self.postMessage({ + type: "error", + message: + err && err.message ? err.message : "Failed to load Python runtime", + }); + // Do not re-throw: the posted "error" message is the sole error + // channel, and re-throwing would reject this catch() promise with no + // live awaiter (readyPromise is already null) — an unhandled rejection. + }, + ); + return; + } + + if (data.type === "run") { + const { id, request } = data; + try { + if (!readyPromise) throw new Error("Pyodide worker not initialized"); + await readyPromise; + // A run that was awaiting readyPromise when init failed lands here with no + // runtime; surface a clean error rather than a null dereference below. + if (!pyodide) throw new Error("Python runtime is not available"); + // JSON-string boundary: avoids PyProxy lifetime management and matches the + // sidecar's JSON contract exactly. + const fn = pyodide.globals.get("run_vector_tool_json"); + // fn is a PyProxy and must be destroyed even if the call throws (e.g. a + // GeoPandas ValueError), or it leaks the underlying Python object. Use + // optional chaining so a missing global doesn't mask the original error. + let out; + try { + out = fn(JSON.stringify(request)); + } finally { + fn?.destroy(); + } + const parsed = JSON.parse(out); + self.postMessage({ + type: "result", + id, + geojson: parsed.geojson, + messages: parsed.messages, + }); + } catch (err) { + self.postMessage({ + type: "error", + id, + message: err && err.message ? err.message : String(err), + }); + } + } +}; diff --git a/apps/geolibre-desktop/public/whitebox-catalog-snapshot.json b/apps/geolibre-desktop/public/whitebox-catalog-snapshot.json new file mode 100644 index 0000000..1e455f8 --- /dev/null +++ b/apps/geolibre-desktop/public/whitebox-catalog-snapshot.json @@ -0,0 +1 @@ +{"tool_count":775,"tools":[{"id":"build_object_hierarchy_multiscale","display_name":"Build Object Hierarchy Multiscale","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"coarse_segments","description":"Coarse Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"fine_segments","description":"Fine Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"classify_objects_ensemble_pro","display_name":"Classify Objects Ensemble Pro","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"features","description":"Features","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"training","description":"Training","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"segment_id_field","description":"Segment Id Field","type":"str","required":false,"default":"segment_id","options":[],"kind":"string"},{"name":"class_field","description":"Class Field","type":"str","required":false,"default":"class","options":[],"kind":"lidar_in"},{"name":"n_trees","description":"N Trees","type":"int","required":false,"default":400,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"classify_objects_random_forest","display_name":"Classify Objects Random Forest","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"features","description":"Features","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"training","description":"Training","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"segment_id_field","description":"Segment Id Field","type":"str","required":false,"default":"segment_id","options":[],"kind":"string"},{"name":"class_field","description":"Class Field","type":"str","required":false,"default":"class","options":[],"kind":"lidar_in"},{"name":"n_trees","description":"N Trees","type":"int","required":false,"default":200,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"classify_objects_rules_basic","display_name":"Classify Objects Rules Basic","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"features","description":"Features","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"rules","description":"Rules","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"default_class","description":"Default Class","type":"str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"classify_objects_rules_hierarchical","display_name":"Classify Objects Rules Hierarchical","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"features","description":"Features","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"rules","description":"Rules","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"default_class","description":"Default Class","type":"str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"classify_objects_svm","display_name":"Classify Objects Svm","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"features","description":"Features","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"training","description":"Training","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"segment_id_field","description":"Segment Id Field","type":"str","required":false,"default":"segment_id","options":[],"kind":"string"},{"name":"class_field","description":"Class Field","type":"str","required":false,"default":"class","options":[],"kind":"lidar_in"},{"name":"n_trees","description":"N Trees","type":"int","required":false,"default":200,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"evaluate_object_classification_accuracy","display_name":"Evaluate Object Classification Accuracy","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"predictions","description":"Predictions","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"reference","description":"Reference","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"segment_id_field","description":"Segment Id Field","type":"str","required":false,"default":"segment_id","options":[],"kind":"string"},{"name":"predicted_field","description":"Predicted Field","type":"str","required":false,"default":"predicted_class","options":[],"kind":"string"},{"name":"reference_field","description":"Reference Field","type":"str","required":false,"default":"class","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"evaluate_segmentation_quality_pro","display_name":"Evaluate Segmentation Quality Pro","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"segments","description":"Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"reference","description":"Reference","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"image_segmentation","display_name":"Image Segmentation","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"threshold","description":"Threshold","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"steps","description":"Steps","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"min_area","description":"Min Area","type":"int","required":false,"default":4,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"image_slider","display_name":"Image Slider","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"left_raster","description":"Left Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"right_raster","description":"Right Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output_html_file","description":"Output Html File","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"left_label","description":"Left Label","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"right_label","description":"Right Label","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"image_height","description":"Image Height","type":"int","required":false,"default":600,"options":[],"kind":"int"}],"return_type":"str"},{"id":"image_stack_profile","display_name":"Image Stack Profile","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output_html","description":"Output Html","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"obia_audit_report_pro","display_name":"Obia Audit Report Pro","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"artifacts","description":"Artifacts","type":"list[str]","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"obia_batch_orchestrator_pro","display_name":"Obia Batch Orchestrator Pro","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"jobs","description":"Jobs","type":"list[dict[str, Any]]","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"dict[str, Any]"},{"id":"obia_pipeline_basic","display_name":"Obia Pipeline Basic","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training","description":"Training","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output Prefix","type":"str","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"segment_method","description":"Segment Method","type":"str","required":false,"default":"slic","options":[],"kind":"string"},{"name":"min_size","description":"Min Size","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"class_field","description":"Class Field","type":"str","required":false,"default":"class","options":[],"kind":"lidar_in"}],"return_type":"dict[str, Any]"},{"id":"object_class_probability_maps","display_name":"Object Class Probability Maps","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"predictions","description":"Predictions","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"object_features_context_neighbors","display_name":"Object Features Context Neighbors","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"segments","description":"Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"object_features_shape_basic","display_name":"Object Features Shape Basic","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"segments","description":"Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"object_features_spectral_basic","display_name":"Object Features Spectral Basic","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"segments","description":"Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"object_features_texture_glcm_basic","display_name":"Object Features Texture Glcm Basic","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"segments","description":"Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"levels","description":"Levels","type":"int","required":false,"default":16,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"object_features_topology_relations","display_name":"Object Features Topology Relations","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"segments","description":"Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"object_uncertainty_diagnostics_pro","display_name":"Object Uncertainty Diagnostics Pro","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"probabilities","description":"Probabilities","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"low_conf_threshold","description":"Low Conf Threshold","type":"float","required":false,"default":0.7,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"objects_boundary_refinement_pro","display_name":"Objects Boundary Refinement Pro","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"segments","description":"Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"iterations","description":"Iterations","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"min_size","description":"Min Size","type":"int","required":false,"default":6,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"objects_enforce_min_mapping_unit","display_name":"Objects Enforce Min Mapping Unit","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"segments","description":"Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_size","description":"Min Size","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"method","description":"Method","type":"str","required":false,"default":"longest","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"polygons_to_segments","display_name":"Polygons To Segments","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Cell Size","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"field","description":"Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"propagate_labels_across_hierarchy","display_name":"Propagate Labels Across Hierarchy","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"hierarchy","description":"Hierarchy","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"parent_labels","description":"Parent Labels","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"child_labels","description":"Child Labels","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"segment_graph_felzenszwalb","display_name":"Segment Graph Felzenszwalb","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"k","description":"K","type":"float","required":false,"default":500,"options":[],"kind":"double"},{"name":"sigma","description":"Sigma","type":"float","required":false,"default":0.8,"options":[],"kind":"double"},{"name":"min_area","description":"Min Area","type":"int","required":false,"default":20,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"segment_multiresolution_hierarchical","display_name":"Segment Multiresolution Hierarchical","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"coarse_k","description":"Coarse K","type":"float","required":false,"default":800,"options":[],"kind":"double"},{"name":"fine_k","description":"Fine K","type":"float","required":false,"default":250,"options":[],"kind":"double"},{"name":"output_prefix","description":"Output Prefix","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster, str]"},{"id":"segment_scale_parameter_optimizer","display_name":"Segment Scale Parameter Optimizer","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"candidate_scales","description":"Candidate Scales","type":"list[float] | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"segment_slic_superpixels","display_name":"Segment Slic Superpixels","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"region_size","description":"Region Size","type":"int","required":false,"default":20,"options":[],"kind":"int"},{"name":"compactness","description":"Compactness","type":"float","required":false,"default":10,"options":[],"kind":"double"},{"name":"min_area","description":"Min Area","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"segment_watershed_markers","display_name":"Segment Watershed Markers","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"gradient_weight","description":"Gradient Weight","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"min_area","description":"Min Area","type":"int","required":false,"default":12,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"segments_merge_small_regions","display_name":"Segments Merge Small Regions","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"segments","description":"Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_size","description":"Min Size","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"method","description":"Method","type":"str","required":false,"default":"longest","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"segments_split_low_cohesion","display_name":"Segments Split Low Cohesion","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"segments","description":"Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"split_scale","description":"Split Scale","type":"float","required":false,"default":120,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"segments_to_polygons","display_name":"Segments To Polygons","summary":"","category":"Remote Sensing - OBIA","taxonomy_category":"remote_sensing","taxonomy_subcategory":"obia","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"segments","description":"Segments","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"field","description":"Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"evaluate_training_sites","display_name":"Evaluate Training Sites","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"class_field_name","description":"Class Field Name","type":"str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"fuzzy_knn_classification","display_name":"Fuzzy Knn Classification","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"class_field_name","description":"Class Field Name","type":"str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"k","description":"K","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"m","description":"M","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"probability_output","description":"Probability Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"tuple[Raster, Raster]"},{"id":"generalize_classified_raster","display_name":"Generalize Classified Raster","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"area_threshold","description":"Area Threshold","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"method","description":"Method","type":"str","required":false,"default":"longest","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"generalize_with_similarity","display_name":"Generalize With Similarity","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"similarity_rasters","description":"Similarity Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_size","description":"Min Size","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"k_means_clustering","display_name":"K Means Clustering","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"classes","description":"Classes","type":"int","required":true,"default":null,"options":[],"kind":"int"},{"name":"max_iterations","description":"Max Iterations","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"class_change","description":"Class Change","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"initialize","description":"Initialize","type":"str","required":false,"default":"diagonal","options":[],"kind":"string"},{"name":"min_class_size","description":"Min Class Size","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"out_html","description":"Out Html","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, int, str | None]"},{"id":"knn_classification","display_name":"Knn Classification","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"class_field_name","description":"Class Field Name","type":"str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"k","description":"K","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"use_clipping","description":"Use Clipping","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"knn_regression","display_name":"Knn Regression","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"k","description":"K","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"distance_weighting","description":"Distance Weighting","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"logistic_regression","display_name":"Logistic Regression","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"class_field_name","description":"Class Field Name","type":"str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"alpha","description":"Alpha","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"min_dist_classification","display_name":"Min Dist Classification","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"class_field_name","description":"Class Field Name","type":"str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"dist_threshold","description":"Dist Threshold","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"modified_k_means_clustering","display_name":"Modified K Means Clustering","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"merge_dist","description":"Merge Dist","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"start_clusters","description":"Start Clusters","type":"int","required":false,"default":1000,"options":[],"kind":"int"},{"name":"max_iterations","description":"Max Iterations","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"class_change","description":"Class Change","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"out_html","description":"Out Html","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, int, str | None]"},{"id":"nnd_classification","display_name":"Nnd Classification","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"class_field_name","description":"Class Field Name","type":"str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"z_threshold","description":"Z Threshold","type":"float","required":false,"default":1.96,"options":[],"kind":"double"},{"name":"outlier_is_zero","description":"Outlier Is Zero","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"k","description":"K","type":"int","required":false,"default":25,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"otsu_thresholding","display_name":"Otsu Thresholding","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"parallelepiped_classification","display_name":"Parallelepiped Classification","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"class_field_name","description":"Class Field Name","type":"str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"random_forest_classification","display_name":"Random Forest Classification","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"class_field_name","description":"Class Field Name","type":"str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"n_trees","description":"N Trees","type":"int","required":false,"default":200,"options":[],"kind":"int"},{"name":"min_samples_leaf","description":"Min Samples Leaf","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"min_samples_split","description":"Min Samples Split","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"random_forest_regression","display_name":"Random Forest Regression","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"n_trees","description":"N Trees","type":"int","required":false,"default":200,"options":[],"kind":"int"},{"name":"min_samples_leaf","description":"Min Samples Leaf","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"min_samples_split","description":"Min Samples Split","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"svm_classification","display_name":"Svm Classification","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"class_field_name","description":"Class Field Name","type":"str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"kernel","description":"Kernel","type":"str","required":false,"default":"linear","options":[],"kind":"string"},{"name":"c","description":"C","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"gamma","description":"Gamma","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"epoch","description":"Epoch","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"svm_regression","display_name":"Svm Regression","summary":"","category":"Remote Sensing - Classification","taxonomy_category":"remote_sensing","taxonomy_subcategory":"classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"kernel","description":"Kernel","type":"str","required":false,"default":"linear","options":[],"kind":"string"},{"name":"c","description":"C","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"gamma","description":"Gamma","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"eps","description":"Eps","type":"float","required":false,"default":0.1,"options":[],"kind":"double"},{"name":"tol","description":"Tol","type":"float","required":false,"default":0.001,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"change_vector_analysis","display_name":"Change Vector Analysis","summary":"","category":"Remote Sensing - Change Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"change_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"date1","description":"Date1","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"date2","description":"Date2","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"magnitude_output","description":"Magnitude Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"direction_output","description":"Direction Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"image_difference_change_detection","display_name":"Image Difference Change Detection","summary":"","category":"Remote Sensing - Change Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"change_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"t1_inputs","description":"T1 Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"t2_inputs","description":"T2 Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"mode","description":"Mode","type":"str","required":false,"default":"magnitude","options":[],"kind":"string"},{"name":"threshold_sigma","description":"Threshold Sigma","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_absolute","description":"Output Absolute","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_signed","description":"Output Signed","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_mask","description":"Output Mask","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"}],"return_type":"Any"},{"id":"pca_based_change_detection","display_name":"Pca Based Change Detection","summary":"","category":"Remote Sensing - Change Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"change_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"t1_inputs","description":"T1 Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"t2_inputs","description":"T2 Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"component","description":"Component","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"standardized","description":"Standardized","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"threshold_sigma","description":"Threshold Sigma","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_mask","description":"Output Mask","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_report","description":"Output Report","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"}],"return_type":"Any"},{"id":"post_classification_change","display_name":"Post Classification Change","summary":"","category":"Remote Sensing - Change Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"change_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"t1_classified","description":"T1 Classified","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"t2_classified","description":"T2 Classified","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"transition_scale","description":"Transition Scale","type":"int","required":false,"default":1000,"options":[],"kind":"int"},{"name":"t1_class_remap","description":"T1 Class Remap","type":"dict[Any, Any] | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"t2_class_remap","description":"T2 Class Remap","type":"dict[Any, Any] | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"remote_sensing_change_detection","display_name":"Remote Sensing Change Detection","summary":"","category":"Remote Sensing - Change Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"change_detection","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"baseline_bundle","description":"Baseline Bundle","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"change_bundle","description":"Change Bundle","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"baseline_red_band_index","description":"Baseline Red Band Index","type":"int","required":false,"default":0,"options":[],"kind":"int"},{"name":"baseline_nir_band_index","description":"Baseline Nir Band Index","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"change_red_band_index","description":"Change Red Band Index","type":"int","required":false,"default":0,"options":[],"kind":"int"},{"name":"change_nir_band_index","description":"Change Nir Band Index","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"intermediate_ndvi","description":"Intermediate Ndvi","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"profile","description":"Profile","type":"str","required":false,"default":"balanced","options":[],"kind":"file_in"},{"name":"high_confidence_threshold","description":"High Confidence Threshold","type":"float","required":false,"default":0.85,"options":[],"kind":"double"},{"name":"output_prefix","description":"Output Prefix","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster, str]"},{"id":"time_series_change_intelligence","display_name":"Time Series Change Intelligence","summary":"","category":"Remote Sensing - Change Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"change_detection","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"input_stack","description":"Input Stack","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"qa_stack","description":"Qa Stack","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"algorithm_mode","description":"Algorithm Mode","type":"str","required":false,"default":"fast","options":[],"kind":"string"},{"name":"min_observations","description":"Min Observations","type":"int","required":false,"default":24,"options":[],"kind":"int"},{"name":"output_prefix","description":"Output Prefix","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster, Raster, Raster, str]"},{"id":"write_function_memory_insertion","display_name":"Write Function Memory Insertion","summary":"","category":"Remote Sensing - Change Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"change_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input3","description":"Input3","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"brdf_surface_reflectance_consistency","display_name":"Brdf Surface Reflectance Consistency","summary":"","category":"Remote Sensing - Radiometric Correction","taxonomy_category":"remote_sensing","taxonomy_subcategory":"radiometric_correction","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"input_red","description":"Input Red","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input_nir","description":"Input Nir","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input_dem","description":"Input Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"solar_zenith_deg","description":"Solar Zenith Deg","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"solar_azimuth_deg","description":"Solar Azimuth Deg","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"input_green","description":"Input Green","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"profile","description":"Profile","type":"str","required":false,"default":"balanced","options":[],"kind":"file_in"},{"name":"output_prefix","description":"Output Prefix","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster, Raster, str]"},{"id":"brdf_normalization","display_name":"Brdf Normalization","summary":"","category":"Remote Sensing - Radiometric Correction","taxonomy_category":"remote_sensing","taxonomy_subcategory":"radiometric_correction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_raster","description":"Input reflectance raster to normalize (single band or multi-band stack).","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input_dem","description":"DEM raster co-registered with the input (same grid or will be resampled to match).","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"solar_zenith_deg","description":"Solar zenith angle in degrees at acquisition time.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"solar_azimuth_deg","description":"Solar azimuth angle in degrees (clockwise from north) at acquisition time.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"method","description":"Correction method: c_correction (default) or minnaert.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"minnaert_k","description":"Minnaert k coefficient in [0,1] (default 0.5). Only used when method=minnaert.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"z_factor","description":"Vertical exaggeration for DEM slope/aspect computation (default 1.0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output_prefix","description":"Prefix for output files (default brdf_normalized).","type":"file","required":false,"default":null,"options":[],"kind":"file_out"},{"name":"output","description":"Output path for normalized raster (overrides output_prefix for single-output mode).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"correct_vignetting","display_name":"Correct Vignetting","summary":"","category":"Remote Sensing - Radiometric Correction","taxonomy_category":"remote_sensing","taxonomy_subcategory":"radiometric_correction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"pp","description":"Pp","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"focal_length","description":"Focal Length","type":"float","required":false,"default":304.8,"options":[],"kind":"double"},{"name":"image_width","description":"Image Width","type":"float","required":false,"default":228.6,"options":[],"kind":"double"},{"name":"n","description":"N","type":"float","required":false,"default":4,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"dark_object_subtraction","display_name":"Dark Object Subtraction","summary":"","category":"Remote Sensing - Radiometric Correction","taxonomy_category":"remote_sensing","taxonomy_subcategory":"radiometric_correction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"percentile","description":"Percentile","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"clamp_non_negative","description":"Clamp Non Negative","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_diagnostic_offsets","description":"Output Diagnostic Offsets","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"dn_to_toa_reflectance","display_name":"Dn To Toa Reflectance","summary":"","category":"Remote Sensing - Radiometric Correction","taxonomy_category":"remote_sensing","taxonomy_subcategory":"radiometric_correction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"reflectance_mult","description":"Reflectance Mult","type":"Any | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"reflectance_add","description":"Reflectance Add","type":"Any | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"sensor_bundle_root","description":"Sensor Bundle Root","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"sun_elevation_deg","description":"Sun Elevation Deg","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"apply_solar_correction","description":"Apply Solar Correction","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"clamp_unit_interval","description":"Clamp Unit Interval","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"terrain_corrected_optical_analytics","display_name":"Terrain Corrected Optical Analytics","summary":"","category":"Remote Sensing - Radiometric Correction","taxonomy_category":"remote_sensing","taxonomy_subcategory":"radiometric_correction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_dem","description":"DEM raster co-registered with optical bands.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"bundle_root","description":"Optional optical sensor bundle root directory (currently Sentinel-2 SAFE, Landsat, or DIMAP SPOT/Pleiades) used to auto-resolve required bands, QA when available, and solar metadata when explicit inputs are omitted.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"input_red","description":"Red band raster (optional when bundle_root is provided).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"input_nir","description":"NIR band raster (optional when bundle_root is provided).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"input_green","description":"Green band raster (optional).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"input_blue","description":"Blue band raster (optional). Auto-resolved from bundle metadata when bundle_root is provided.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"solar_mode","description":"Solar-angle resolution mode: auto, manual, metadata, datetime_location.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"solar_zenith_deg","description":"Solar zenith angle in degrees (required in manual mode).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"solar_azimuth_deg","description":"Solar azimuth in degrees (clockwise from north, required in manual mode).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"acquisition_datetime_utc","description":"Acquisition timestamp in RFC3339 UTC for datetime_location mode.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"latitude","description":"Optional latitude for datetime_location mode.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"longitude","description":"Optional longitude for datetime_location mode.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"profile","description":"Processing profile: conservative, balanced, fast.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cloud_threshold","description":"Optional cloud threshold in source band units. Auto-inferred when omitted.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"shadow_threshold","description":"Optional cloud-shadow threshold in source band units. Auto-inferred when omitted.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"qa_mask","description":"Optional QA mask raster path.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"qa_mask_format","description":"QA mask encoding: auto, landsat_qa_pixel, sentinel2_scl, sentinel2_qa60, binary.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"mask_strategy","description":"Mask strategy: auto, qa_only, heuristic_only, qa_plus_heuristic.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"z_factor","description":"Vertical exaggeration for slope/aspect.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output_prefix","description":"Prefix for all output artifacts.","type":"file","required":false,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"land_surface_temperature_single_channel","display_name":"Land Surface Temperature Single Channel","summary":"","category":"Remote Sensing - Thermal & Emissivity","taxonomy_category":"remote_sensing","taxonomy_subcategory":"thermal_emissivity","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"thermal_input","description":"Thermal Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input_is_brightness_temp","description":"Input Is Brightness Temp","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"emissivity_input","description":"Emissivity Input","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"emissivity_constant","description":"Emissivity Constant","type":"float","required":false,"default":0.98,"options":[],"kind":"double"},{"name":"sensor_bundle_root","description":"Sensor Bundle Root","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"thermal_band_number","description":"Thermal Band Number","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"radiance_mult","description":"Radiance Mult","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"radiance_add","description":"Radiance Add","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"k1_constant","description":"K1 Constant","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"k2_constant","description":"K2 Constant","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"wavelength_um","description":"Wavelength Um","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output_units","description":"Output Units","type":"str","required":false,"default":"celsius","options":[],"kind":"string"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"land_surface_temperature_split_window","display_name":"Land Surface Temperature Split Window","summary":"","category":"Remote Sensing - Thermal & Emissivity","taxonomy_category":"remote_sensing","taxonomy_subcategory":"thermal_emissivity","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"thermal1_input","description":"Thermal1 Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"thermal2_input","description":"Thermal2 Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input_is_brightness_temp","description":"Input Is Brightness Temp","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"emissivity_mean_input","description":"Emissivity Mean Input","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"emissivity_delta_input","description":"Emissivity Delta Input","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"emissivity_mean_constant","description":"Emissivity Mean Constant","type":"float","required":false,"default":0.98,"options":[],"kind":"double"},{"name":"emissivity_delta_constant","description":"Emissivity Delta Constant","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"sensor_bundle_root","description":"Sensor Bundle Root","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"thermal_band1_number","description":"Thermal Band1 Number","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"thermal_band2_number","description":"Thermal Band2 Number","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"radiance1_mult","description":"Radiance1 Mult","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"radiance1_add","description":"Radiance1 Add","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"k1_1","description":"K1 1","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"k2_1","description":"K2 1","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"radiance2_mult","description":"Radiance2 Mult","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"radiance2_add","description":"Radiance2 Add","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"k1_2","description":"K1 2","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"k2_2","description":"K2 2","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"coeff_a0","description":"Coeff A0","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"coeff_a1","description":"Coeff A1","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"coeff_a2","description":"Coeff A2","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"coeff_a3","description":"Coeff A3","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"coeff_a4","description":"Coeff A4","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"coeff_a5","description":"Coeff A5","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output_units","description":"Output Units","type":"str","required":false,"default":"celsius","options":[],"kind":"string"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"ndvi_based_emissivity","display_name":"Ndvi Based Emissivity","summary":"","category":"Remote Sensing - Thermal & Emissivity","taxonomy_category":"remote_sensing","taxonomy_subcategory":"thermal_emissivity","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"red_input","description":"Red Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"nir_input","description":"Nir Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"ndvi_soil","description":"Ndvi Soil","type":"float","required":false,"default":0.2,"options":[],"kind":"double"},{"name":"ndvi_vegetation","description":"Ndvi Vegetation","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"emissivity_soil","description":"Emissivity Soil","type":"float","required":false,"default":0.97,"options":[],"kind":"double"},{"name":"emissivity_vegetation","description":"Emissivity Vegetation","type":"float","required":false,"default":0.99,"options":[],"kind":"double"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"continuum_removal","display_name":"Continuum Removal","summary":"","category":"Remote Sensing - Spectral Analytics","taxonomy_category":"remote_sensing","taxonomy_subcategory":"spectral_analytics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"wavelengths","description":"Wavelengths","type":"Any | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"linear_spectral_unmixing","display_name":"Linear Spectral Unmixing","summary":"","category":"Remote Sensing - Spectral Analytics","taxonomy_category":"remote_sensing","taxonomy_subcategory":"spectral_analytics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"endmembers","description":"Endmembers","type":"Any","required":true,"default":null,"options":[],"kind":"string"},{"name":"sum_to_one","description":"Sum To One","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"iterations","description":"Iterations","type":"int","required":false,"default":80,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"float","required":false,"default":0.05,"options":[],"kind":"double"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_residual","description":"Output Residual","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"minimum_noise_fraction","display_name":"Minimum Noise Fraction","summary":"","category":"Remote Sensing - Spectral Analytics","taxonomy_category":"remote_sensing","taxonomy_subcategory":"spectral_analytics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"num_components","description":"Num Components","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"noise_mode","description":"Noise Mode","type":"str","required":false,"default":"difference_x","options":[],"kind":"string"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_inverse","description":"Output Inverse","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"spectral_angle_mapper","display_name":"Spectral Angle Mapper","summary":"","category":"Remote Sensing - Spectral Analytics","taxonomy_category":"remote_sensing","taxonomy_subcategory":"spectral_analytics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"endmembers","description":"Endmembers","type":"Any","required":true,"default":null,"options":[],"kind":"string"},{"name":"threshold_angle_deg","description":"Threshold Angle Deg","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_angle","description":"Output Angle","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"spectral_library_matching","display_name":"Spectral Library Matching","summary":"","category":"Remote Sensing - Spectral Analytics","taxonomy_category":"remote_sensing","taxonomy_subcategory":"spectral_analytics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"library","description":"Library","type":"Any | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"library_csv","description":"Library Csv","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"metric","description":"Metric","type":"str","required":false,"default":"sam","options":[],"kind":"string"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_score","description":"Output Score","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"canny_edge_detection","display_name":"Canny Edge Detection","summary":"","category":"Remote Sensing - Edge & Feature Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"edge_feature_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"sigma","description":"Sigma","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"low_threshold","description":"Low Threshold","type":"float","required":false,"default":0.05,"options":[],"kind":"double"},{"name":"high_threshold","description":"High Threshold","type":"float","required":false,"default":0.15,"options":[],"kind":"double"},{"name":"add_back","description":"Add Back","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"corner_detection","display_name":"Corner Detection","summary":"","category":"Remote Sensing - Edge & Feature Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"edge_feature_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"laplacian_filter","display_name":"Laplacian Filter","summary":"","category":"Remote Sensing - Edge & Feature Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"edge_feature_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"variant","description":"Variant","type":"str","required":false,"default":"3x3(1)","options":[],"kind":"string"},{"name":"clip_amount","description":"Clip Amount","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"laplacian_of_gaussians_filter","display_name":"Laplacian Of Gaussians Filter","summary":"","category":"Remote Sensing - Edge & Feature Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"edge_feature_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"sigma","description":"Sigma","type":"float","required":false,"default":0.75,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"prewitt_filter","display_name":"Prewitt Filter","summary":"","category":"Remote Sensing - Edge & Feature Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"edge_feature_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"clip_tails","description":"Clip Tails","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"roberts_cross_filter","display_name":"Roberts Cross Filter","summary":"","category":"Remote Sensing - Edge & Feature Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"edge_feature_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"clip_amount","description":"Clip Amount","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"sobel_filter","display_name":"Sobel Filter","summary":"","category":"Remote Sensing - Edge & Feature Detection","taxonomy_category":"remote_sensing","taxonomy_subcategory":"edge_feature_detection","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"variant","description":"Variant","type":"str","required":false,"default":"3x3","options":[],"kind":"string"},{"name":"clip_tails","description":"Clip Tails","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"balance_contrast_enhancement","display_name":"Balance Contrast Enhancement","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"image","description":"Image","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"band_mean","description":"Band Mean","type":"float","required":false,"default":100,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"create_colour_composite","display_name":"Create Colour Composite","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"red","description":"Red","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"green","description":"Green","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"blue","description":"Blue","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"opacity","description":"Opacity","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"enhance","description":"Enhance","type":"bool | None","required":false,"default":null,"options":[],"kind":"bool"},{"name":"treat_zeros_as_nodata","description":"Treat Zeros As Nodata","type":"bool | None","required":false,"default":null,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"direct_decorrelation_stretch","display_name":"Direct Decorrelation Stretch","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"image","description":"Image","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"achromatic_factor","description":"Achromatic Factor","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"clip_percent","description":"Clip Percent","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"false_colour_composite","display_name":"False Colour Composite","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"wbe","description":"Wbe","type":"Any","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"Any","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"gamma_correction","display_name":"Gamma Correction","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"gamma_value","description":"Gamma Value","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"gaussian_contrast_stretch","display_name":"Gaussian Contrast Stretch","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"num_tones","description":"Num Tones","type":"int","required":false,"default":256,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"histogram_equalization","display_name":"Histogram Equalization","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"num_tones","description":"Num Tones","type":"int","required":false,"default":256,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"histogram_matching","display_name":"Histogram Matching","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"image","description":"Image","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"histogram","description":"Histogram","type":"list[list[float]]","required":true,"default":null,"options":[],"kind":"double"},{"name":"histo_is_cumulative","description":"Histo Is Cumulative","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"histogram_matching_two_images","display_name":"Histogram Matching Two Images","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"reference","description":"Reference","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"ihs_to_rgb","display_name":"Ihs To Rgb","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"intensity","description":"Intensity","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"hue","description":"Hue","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"saturation","description":"Saturation","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"red_output","description":"Red Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"green_output","description":"Green Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"blue_output","description":"Blue Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster, Raster]"},{"id":"min_max_contrast_stretch","display_name":"Min Max Contrast Stretch","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_val","description":"Min Val","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"max_val","description":"Max Val","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"num_tones","description":"Num Tones","type":"int","required":false,"default":256,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"mosaic","display_name":"Mosaic","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"method","description":"Method","type":"str","required":false,"default":"nn","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"mosaic_with_feathering","display_name":"Mosaic With Feathering","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"method","description":"Method","type":"str","required":false,"default":"cc","options":[],"kind":"string"},{"name":"weight","description":"Weight","type":"float","required":false,"default":4,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"normalized_difference_index","display_name":"Normalized Difference Index","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"nir_image","description":"Nir Image","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"red_image","description":"Red Image","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"clip_percent","description":"Clip Percent","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"correction_value","description":"Correction Value","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"panchromatic_sharpening","display_name":"Panchromatic Sharpening","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"pan","description":"Pan","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"red","description":"Red","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"green","description":"Green","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"blue","description":"Blue","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"composite","description":"Composite","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"method","description":"Method","type":"str","required":false,"default":"brovey","options":[],"kind":"string"},{"name":"output_mode","description":"Output Mode","type":"str","required":false,"default":"packed","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"percentage_contrast_stretch","display_name":"Percentage Contrast Stretch","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"clip","description":"Clip","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"tail","description":"Tail","type":"str","required":false,"default":"both","options":[],"kind":"string"},{"name":"num_tones","description":"Num Tones","type":"int","required":false,"default":256,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"piecewise_contrast_stretch","display_name":"Piecewise Contrast Stretch","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"transformation_statement","description":"Transformation Statement","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"num_greytones","description":"Num Greytones","type":"float","required":false,"default":1024,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"resample","display_name":"Resample","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"base","description":"Base","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"cell_size","description":"Cell Size","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"method","description":"Method","type":"str","required":false,"default":"cc","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"rgb_to_ihs","display_name":"Rgb To Ihs","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"red","description":"Red","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"green","description":"Green","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"blue","description":"Blue","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"composite","description":"Composite","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"intensity_output","description":"Intensity Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"hue_output","description":"Hue Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"saturation_output","description":"Saturation Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster, Raster]"},{"id":"sigmoidal_contrast_stretch","display_name":"Sigmoidal Contrast Stretch","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"cutoff","description":"Cutoff","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"gain","description":"Gain","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"num_tones","description":"Num Tones","type":"int","required":false,"default":256,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"split_colour_composite","display_name":"Split Colour Composite","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"red_output","description":"Red Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"green_output","description":"Green Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"blue_output","description":"Blue Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster, Raster]"},{"id":"standard_deviation_contrast_stretch","display_name":"Standard Deviation Contrast Stretch","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"clip","description":"Clip","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"num_tones","description":"Num Tones","type":"int","required":false,"default":256,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"true_colour_composite","display_name":"True Colour Composite","summary":"","category":"Remote Sensing - Enhancement & Contrast","taxonomy_category":"remote_sensing","taxonomy_subcategory":"enhancement_contrast","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"wbe","description":"Wbe","type":"Any","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"Any","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"adaptive_filter","display_name":"Adaptive Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"threshold","description":"Threshold","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"anisotropic_diffusion_filter","display_name":"Anisotropic Diffusion Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"iterations","description":"Iterations","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"kappa","description":"Kappa","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"lambda_","description":"Lambda","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"bilateral_filter","display_name":"Bilateral Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"sigma_dist","description":"Sigma Dist","type":"float","required":false,"default":0.75,"options":[],"kind":"double"},{"name":"sigma_int","description":"Sigma Int","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"closing","display_name":"Closing","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"conservative_smoothing_filter","display_name":"Conservative Smoothing Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":3,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"diff_of_gaussians_filter","display_name":"Diff Of Gaussians Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"sigma1","description":"Sigma1","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"sigma2","description":"Sigma2","type":"float","required":false,"default":4,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"diversity_filter","display_name":"Diversity Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"edge_preserving_mean_filter","display_name":"Edge Preserving Mean Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"threshold","description":"Threshold","type":"float","required":false,"default":15,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"emboss_filter","display_name":"Emboss Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"direction","description":"Direction","type":"Literal[\"n\", \"s\", \"e\", \"w\", \"ne\", \"nw\", \"se\", \"sw\"]","required":false,"default":"n","options":[],"kind":"string"},{"name":"clip_amount","description":"Clip Amount","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"fast_almost_gaussian_filter","display_name":"Fast Almost Gaussian Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"sigma","description":"Sigma","type":"float","required":false,"default":1.8,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"flip_image","display_name":"Flip Image","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"direction","description":"Direction","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"frangi_filter","display_name":"Frangi Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"scales","description":"Scales","type":"list[float] | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"beta","description":"Beta","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"c","description":"C","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"gabor_filter_bank","display_name":"Gabor Filter Bank","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"sigma","description":"Sigma","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"frequency","description":"Frequency","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"orientations","description":"Orientations","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"gaussian_filter","display_name":"Gaussian Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"sigma","description":"Sigma","type":"float","required":false,"default":0.75,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"glcm_texture","display_name":"Glcm Texture","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"window_size","description":"Window Size","type":"int","required":false,"default":7,"options":[],"kind":"int"},{"name":"distance","description":"Distance","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"angles","description":"Angles","type":"str","required":false,"default":"0,45,90,135","options":[],"kind":"string"},{"name":"features","description":"Features","type":"str","required":false,"default":"contrast,homogeneity,energy,entropy","options":[],"kind":"string"},{"name":"direction_aggregation","description":"Direction Aggregation","type":"str","required":false,"default":"mean","options":[],"kind":"string"},{"name":"levels","description":"Levels","type":"int","required":false,"default":32,"options":[],"kind":"int"},{"name":"symmetric","description":"Symmetric","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"guided_filter","display_name":"Guided Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"radius","description":"Radius","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"epsilon","description":"Epsilon","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"high_pass_bilateral_filter","display_name":"High Pass Bilateral Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"sigma_dist","description":"Sigma Dist","type":"float","required":false,"default":0.75,"options":[],"kind":"double"},{"name":"sigma_int","description":"Sigma Int","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"treat_as_rgb","description":"Treat As Rgb","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"assume_three_band_rgb","description":"Assume Three Band Rgb","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"high_pass_filter","display_name":"High Pass Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"high_pass_median_filter","display_name":"High Pass Median Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"sig_digits","description":"Sig Digits","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"integral_image_transform","display_name":"Integral Image Transform","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"k_nearest_mean_filter","display_name":"K Nearest Mean Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":3,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"k","description":"K","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"kuwahara_filter","display_name":"Kuwahara Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"radius","description":"Radius","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lee_filter","display_name":"Lee Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"sigma","description":"Sigma","type":"float","required":false,"default":10,"options":[],"kind":"double"},{"name":"m_value","description":"M Value","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"line_detection_filter","display_name":"Line Detection Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"variant","description":"Variant","type":"str","required":false,"default":"v","options":[],"kind":"string"},{"name":"abs_values","description":"Abs Values","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"clip_tails","description":"Clip Tails","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"line_thinning","display_name":"Line Thinning","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_ground_point_filter","display_name":"Lidar Ground Point Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"min_neighbours","description":"Min Neighbours","type":"int","required":false,"default":0,"options":[],"kind":"int"},{"name":"slope_threshold","description":"Slope Threshold","type":"float","required":false,"default":45,"options":[],"kind":"double"},{"name":"height_threshold","description":"Height Threshold","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"classify","description":"Classify","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"height_above_ground","description":"Height Above Ground","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"majority_filter","display_name":"Majority Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"maximum_filter","display_name":"Maximum Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"mean_filter","display_name":"Mean Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"median_filter","display_name":"Median Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"sig_digits","description":"Sig Digits","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"minimum_filter","display_name":"Minimum Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"non_local_means_filter","display_name":"Non Local Means Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"search_radius","description":"Search Radius","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"patch_radius","description":"Patch Radius","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"h","description":"H","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"opening","display_name":"Opening","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"olympic_filter","display_name":"Olympic Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"percentile_filter","display_name":"Percentile Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"sig_digits","description":"Sig Digits","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"range_filter","display_name":"Range Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"remove_spurs","display_name":"Remove Spurs","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"max_iterations","description":"Max Iterations","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"savitzky_golay_2d_filter","display_name":"Savitzky Golay 2d Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"window_size","description":"Window Size","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"scharr_filter","display_name":"Scharr Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"clip_tails","description":"Clip Tails","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"standard_deviation_filter","display_name":"Standard Deviation Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"thicken_raster_line","display_name":"Thicken Raster Line","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"tophat_transform","display_name":"Tophat Transform","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"variant","description":"Variant","type":"str","required":false,"default":"white","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"total_filter","display_name":"Total Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"unsharp_masking","display_name":"Unsharp Masking","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"sigma","description":"Sigma","type":"float","required":false,"default":0.75,"options":[],"kind":"double"},{"name":"amount","description":"Amount","type":"float","required":false,"default":100,"options":[],"kind":"double"},{"name":"threshold","description":"Threshold","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"user_defined_weights_filter","display_name":"User Defined Weights Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"weights","description":"Weights","type":"list[list[float]]","required":true,"default":null,"options":[],"kind":"double"},{"name":"kernel_center","description":"Kernel Center","type":"str","required":false,"default":"center","options":[],"kind":"string"},{"name":"normalize_weights","description":"Normalize Weights","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"wiener_filter","display_name":"Wiener Filter","summary":"","category":"Remote Sensing - Filters","taxonomy_category":"remote_sensing","taxonomy_subcategory":"filters","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"radius","description":"Radius","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"noise_variance","description":"Noise Variance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"cloude_pottier_decomposition","display_name":"Cloude Pottier Decomposition","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"matrix_format","description":"Matrix Format","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"enhanced_lee_filter","display_name":"Enhanced Lee Filter","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"enl","description":"Enl","type":"float","required":false,"default":4,"options":[],"kind":"double"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"freeman_durden_decomposition","display_name":"Freeman Durden Decomposition","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"matrix_format","description":"Matrix Format","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_clip_mask","description":"Output Clip Mask","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"frost_filter","display_name":"Frost Filter","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"radius","description":"Radius","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"damping_factor","description":"Damping Factor","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"gamma_map_filter","display_name":"Gamma Map Filter","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"radius","description":"Radius","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"enl","description":"Enl","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"h_alpha_wisart_classification","display_name":"H Alpha Wisart Classification","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"h_raster","description":"H Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"alpha_raster","description":"Alpha Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Raster"},{"id":"kuan_filter","display_name":"Kuan Filter","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"radius","description":"Radius","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"enl","description":"Enl","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"refined_lee_filter","display_name":"Refined Lee Filter","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"sar_analysis_readiness","display_name":"Sar Analysis Readiness","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"input_sar","description":"Input Sar","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"input_sar_bundle","description":"Input Sar Bundle","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"input_measurement_key","description":"Input Measurement Key","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"input_dem","description":"Input Dem","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"pair_sar","description":"Pair Sar","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"pair_sar_bundle","description":"Pair Sar Bundle","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"pair_measurement_key","description":"Pair Measurement Key","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"input_look_angle_deg","description":"Input Look Angle Deg","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"pair_look_angle_deg","description":"Pair Look Angle Deg","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_look_angle_diff_deg","description":"Max Look Angle Diff Deg","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"auto_coregister_pair","description":"Auto Coregister Pair","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"coreg_max_offset_px","description":"Coreg Max Offset Px","type":"int","required":false,"default":24,"options":[],"kind":"int"},{"name":"coreg_decimation","description":"Coreg Decimation","type":"int","required":false,"default":4,"options":[],"kind":"int"},{"name":"coreg_min_overlap_fraction","description":"Coreg Min Overlap Fraction","type":"float","required":false,"default":0.2,"options":[],"kind":"double"},{"name":"speckle_window","description":"Speckle Window","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output_prefix","description":"Output Prefix","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"sar_coregistration","display_name":"Sar Coregistration","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"reference_sar","description":"Reference Sar","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"moving_sar","description":"Moving Sar","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"reference_sar_bundle","description":"Reference Sar Bundle","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"moving_sar_bundle","description":"Moving Sar Bundle","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"coreg_mode","description":"Coreg Mode","type":"Literal[\"translation\", \"affine\", \"local_offset_grid\"]","required":false,"default":"translation","options":[],"kind":"string"},{"name":"max_offset_px","description":"Max Offset Px","type":"int","required":false,"default":32,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"sar_interferogram_coherence","display_name":"Sar Interferogram Coherence","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"reference_sar","description":"Reference Sar","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"moving_sar","description":"Moving Sar","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"reference_sar_real","description":"Reference Sar Real","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"reference_sar_imag","description":"Reference Sar Imag","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"moving_sar_real","description":"Moving Sar Real","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"moving_sar_imag","description":"Moving Sar Imag","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"reference_sar_bundle","description":"Reference Sar Bundle","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"moving_sar_bundle","description":"Moving Sar Bundle","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"wisart_iterative_clustering","display_name":"Wisart Iterative Clustering","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"h_raster","description":"H Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"alpha_raster","description":"Alpha Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"max_iterations","description":"Max Iterations","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"convergence_threshold","description":"Convergence Threshold","type":"float","required":false,"default":0.99,"options":[],"kind":"double"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Raster"},{"id":"yamaguchi_4component_decomposition","display_name":"Yamaguchi 4component Decomposition","summary":"","category":"Remote Sensing - SAR","taxonomy_category":"remote_sensing","taxonomy_subcategory":"sar","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"matrix_format","description":"Matrix Format","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"auto_reproject","description":"Auto Reproject","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"auto_reproject_method","description":"Auto Reproject Method","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"options","description":"Options","type":"dict[str, Any] | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"multi_sensor_fusion_monitoring","display_name":"Multi Sensor Fusion Monitoring","summary":"","category":"Remote Sensing - Workflow Products","taxonomy_category":"remote_sensing","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"baseline_bundle","description":"Baseline multiband optical bundle raster.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"baseline_red_band_index","description":"0-based red band index in baseline_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"baseline_nir_band_index","description":"0-based NIR band index in baseline_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"change_bundle","description":"Change-date multiband optical bundle raster.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"change_red_band_index","description":"0-based red band index in change_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"change_nir_band_index","description":"0-based NIR band index in change_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"input_sar","description":"Input SAR intensity raster.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input_dem","description":"Input DEM raster for terrain context and SAR normalization support.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"pair_sar","description":"Optional second SAR image for coherence-aware agreement.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"thermal_bundle","description":"Optional thermal raster for experimental three-modality fusion.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"thermal_band_index","description":"0-based band index in thermal_bundle (default 0).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"profile","description":"Fusion profile: fast, balanced, conservative.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"harmonization_mode","description":"Cross-sensor bias harmonization mode: off, robust, conservative.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"high_confidence_threshold","description":"Threshold for high-confidence change zoning in [0,1].","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_zone_features","description":"Maximum number of high-confidence change polygons to emit.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"vector_output_format","description":"Vector format for high-confidence zones: gpkg, geojson, or shp (default gpkg).","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"guided_uav_image_intake_workflow","display_name":"Guided Uav Image Intake Workflow","summary":"","category":"Remote Sensing - Workflow Products","taxonomy_category":"remote_sensing","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"images_dir","description":"Input directory containing UAV images.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"profile","description":"[pro] Quality profile (fast, balanced, strict); selects the adaptive-threshold suite that drives pass/review/fail mission classification.","type":"lidar","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"recursive","description":"Recursively scan subdirectories (default true).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"file","required":false,"default":null,"options":[],"kind":"file_out"},{"name":"blur_mode","description":"Blur scoring mode: off, fast, or full.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"registration_oriented_feature_workflow","display_name":"Registration Oriented Feature Workflow","summary":"","category":"Remote Sensing - Workflow Products","taxonomy_category":"remote_sensing","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"mode","description":"pair or set (default set).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"images_dir","description":"Input image directory for mode=set.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"left_image","description":"Left image path for mode=pair.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"right_image","description":"Right image path for mode=pair.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_pairs","description":"Maximum number of planned pairs for mode=set (default 24).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_features_per_image","description":"Maximum keypoints extracted per image (default 500).","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"ratio_test","description":"Descriptor ratio-test threshold in (0,1), default 0.80.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"min_matches","description":"[pro] Minimum accepted match count per pair; drives QA gate, fallback routing, and pass/review/fail classification (default 24).","type":"lidar","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"min_match_count","description":"[pro] Minimum accepted match count per pair for QC classification (default 24).","type":"lidar","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"min_inlier_ratio","description":"[pro] Minimum inlier proxy threshold per pair for QC classification in [0,1] (default 0.18).","type":"lidar","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"qc_policy","description":"[pro] Pair-level QC policy: strict, balanced, permissive (default balanced).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"emit_pair_match_viz","description":"Emit side-by-side pair images with tie-point lines (default false).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_pair_visualizations","description":"Maximum pair visualizations to write when emit_pair_match_viz=true (default 8).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_lines_per_pair","description":"Maximum tie-point lines per visualization (default 150).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"viz_scale","description":"Visualization downscale factor in [0.05, 1.0], default 0.5.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"ascii_to_las","display_name":"Ascii To Las","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_ascii_files","description":"Input Ascii Files","type":"list[str]","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"pattern","description":"Pattern","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"epsg_code","description":"Epsg Code","type":"int","required":false,"default":4326,"options":[],"kind":"int"},{"name":"output_directory","description":"Output Directory","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"las_to_ascii","display_name":"Las To Ascii","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"las_to_shapefile","display_name":"Las To Shapefile","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"output_multipoint","description":"Output Multipoint","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"lidar_colourize","display_name":"Lidar Colourize","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"image","description":"Image","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"lidar_join","display_name":"Lidar Join","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Lidar | str]","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"lidar_shift","display_name":"Lidar Shift","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"x_shift","description":"X Shift","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"y_shift","description":"Y Shift","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"z_shift","description":"Z Shift","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"lidar_tile","display_name":"Lidar Tile","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"tile_width","description":"Tile Width","type":"float","required":false,"default":1000,"options":[],"kind":"double"},{"name":"tile_height","description":"Tile Height","type":"float","required":false,"default":1000,"options":[],"kind":"double"},{"name":"origin_x","description":"Origin X","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"origin_y","description":"Origin Y","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"min_points_in_tile","description":"Min Points In Tile","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"output_laz_format","description":"Output Laz Format","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output_directory","description":"Output Directory","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"lidar_tophat_transform","display_name":"Lidar Tophat Transform","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"search_radius","description":"Search Radius","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"recover_flightline_info","display_name":"Recover Flightline Info","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"max_time_diff","description":"Max Time Diff","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"pt_src_id","description":"Pt Src Id","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"user_data","description":"User Data","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"rgb","description":"Rgb","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"select_tiles_by_polygon","display_name":"Select Tiles By Polygon","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_directory","description":"Input Directory","type":"str","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"output_directory","description":"Output Directory","type":"str","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"polygons","description":"Polygons","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"}],"return_type":"str"},{"id":"sort_lidar","display_name":"Sort Lidar","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"sort_criteria","description":"Sort Criteria","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"split_lidar","display_name":"Split Lidar","summary":"","category":"LiDAR - I/O & Management","taxonomy_category":"lidar","taxonomy_subcategory":"io_management","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"split_criterion","description":"Split Criterion","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"interval","description":"Interval","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"min_pts","description":"Min Pts","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"output_directory","description":"Output Directory","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"classify_buildings_in_lidar","display_name":"Classify Buildings In Lidar","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"in_lidar","description":"In Lidar","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"building_footprints","description":"Building Footprints","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"classify_lidar","display_name":"Classify Lidar","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":2.5,"options":[],"kind":"double"},{"name":"grd_threshold","description":"Grd Threshold","type":"float","required":false,"default":0.1,"options":[],"kind":"double"},{"name":"oto_threshold","description":"Oto Threshold","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"linearity_threshold","description":"Linearity Threshold","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"planarity_threshold","description":"Planarity Threshold","type":"float","required":false,"default":0.85,"options":[],"kind":"double"},{"name":"num_iter","description":"Num Iter","type":"int","required":false,"default":30,"options":[],"kind":"int"},{"name":"facade_threshold","description":"Facade Threshold","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"classify_overlap_points","display_name":"Classify Overlap Points","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"overlap_criterion","description":"Overlap Criterion","type":"str","required":false,"default":"max scan angle","options":[],"kind":"string"},{"name":"filter","description":"Filter","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"clip_lidar_to_polygon","display_name":"Clip Lidar To Polygon","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"polygons","description":"Polygons","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"erase_polygon_from_lidar","display_name":"Erase Polygon From Lidar","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"polygons","description":"Polygons","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"filter_lidar","display_name":"Filter Lidar","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"statement","description":"Statement","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"filter_lidar_by_percentile","display_name":"Filter Lidar By Percentile","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"percentile","description":"Percentile","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"block_size","description":"Block Size","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"filter_lidar_by_reference_surface","display_name":"Filter Lidar By Reference Surface","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"ref_surface","description":"Ref Surface","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"query","description":"Query","type":"str","required":false,"default":"within","options":[],"kind":"string"},{"name":"threshold","description":"Threshold","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"classify","description":"Classify","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"true_class_value","description":"True Class Value","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"false_class_value","description":"False Class Value","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"preserve_classes","description":"Preserve Classes","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"filter_lidar_classes","display_name":"Filter Lidar Classes","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"filter_lidar_noise","display_name":"Filter Lidar Noise","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"filter_lidar_scan_angles","display_name":"Filter Lidar Scan Angles","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"threshold","description":"Threshold","type":"int","required":false,"default":0,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"height_above_ground","display_name":"Height Above Ground","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"improved_ground_point_filter","display_name":"Improved Ground Point Filter","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"block_size","description":"Block Size","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"max_building_size","description":"Max Building Size","type":"float","required":false,"default":150,"options":[],"kind":"double"},{"name":"slope_threshold","description":"Slope Threshold","type":"float","required":false,"default":15,"options":[],"kind":"double"},{"name":"elev_threshold","description":"Elev Threshold","type":"float","required":false,"default":0.15,"options":[],"kind":"double"},{"name":"classify","description":"Classify","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"preserve_classes","description":"Preserve Classes","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"individual_tree_segmentation","display_name":"Individual Tree Segmentation","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"only_use_veg","description":"Only Use Veg","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"veg_classes","description":"Veg Classes","type":"str","required":false,"default":"3,4,5","options":[],"kind":"lidar_in"},{"name":"min_height","description":"Min Height","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"max_height","description":"Max Height","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"bandwidth_min","description":"Bandwidth Min","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"bandwidth_max","description":"Bandwidth Max","type":"float","required":false,"default":6,"options":[],"kind":"double"},{"name":"adaptive_bandwidth","description":"Adaptive Bandwidth","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"adaptive_neighbors","description":"Adaptive Neighbors","type":"int","required":false,"default":24,"options":[],"kind":"int"},{"name":"adaptive_sector_count","description":"Adaptive Sector Count","type":"int","required":false,"default":8,"options":[],"kind":"int"},{"name":"grid_acceleration","description":"Grid Acceleration","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"grid_cell_size","description":"Grid Cell Size","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"grid_refine_exact","description":"Grid Refine Exact","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"grid_refine_iterations","description":"Grid Refine Iterations","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"tile_size","description":"Tile Size","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"tile_overlap","description":"Tile Overlap","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"vertical_bandwidth","description":"Vertical Bandwidth","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"max_iterations","description":"Max Iterations","type":"int","required":false,"default":30,"options":[],"kind":"int"},{"name":"convergence_tol","description":"Convergence Tol","type":"float","required":false,"default":0.05,"options":[],"kind":"double"},{"name":"min_cluster_points","description":"Min Cluster Points","type":"int","required":false,"default":50,"options":[],"kind":"int"},{"name":"mode_merge_dist","description":"Mode Merge Dist","type":"float","required":false,"default":0.8,"options":[],"kind":"double"},{"name":"threads","description":"Threads","type":"int","required":false,"default":0,"options":[],"kind":"int"},{"name":"simd","description":"Simd","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output_id_mode","description":"Output Id Mode","type":"str","required":false,"default":"rgb","options":[],"kind":"string"},{"name":"output_sidecar_csv","description":"Output Sidecar Csv","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"seed","description":"Seed","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"lidar_classify_subset","display_name":"Lidar Classify Subset","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base_lidar","description":"Base Lidar","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"subset_lidar","description":"Subset Lidar","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"subset_class_value","description":"Subset Class Value","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"nonsubset_class_value","description":"Nonsubset Class Value","type":"int","required":false,"default":255,"options":[],"kind":"int"},{"name":"tolerance","description":"Tolerance","type":"float","required":false,"default":0.001,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"lidar_elevation_slice","display_name":"Lidar Elevation Slice","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"minz","description":"Minz","type":"float","required":false,"default":-1.7976931348623157e+308,"options":[],"kind":"double"},{"name":"maxz","description":"Maxz","type":"float","required":false,"default":1.7976931348623157e+308,"options":[],"kind":"double"},{"name":"classify","description":"Classify","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"in_class_value","description":"In Class Value","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"out_class_value","description":"Out Class Value","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"lidar_remove_outliers","display_name":"Lidar Remove Outliers","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"elev_diff","description":"Elev Diff","type":"float","required":false,"default":50,"options":[],"kind":"double"},{"name":"use_median","description":"Use Median","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"classify","description":"Classify","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"lidar_segmentation","display_name":"Lidar Segmentation","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"num_iterations","description":"Num Iterations","type":"int","required":false,"default":50,"options":[],"kind":"int"},{"name":"num_samples","description":"Num Samples","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"inlier_threshold","description":"Inlier Threshold","type":"float","required":false,"default":0.15,"options":[],"kind":"double"},{"name":"acceptable_model_size","description":"Acceptable Model Size","type":"int","required":false,"default":30,"options":[],"kind":"int"},{"name":"max_planar_slope","description":"Max Planar Slope","type":"float","required":false,"default":75,"options":[],"kind":"double"},{"name":"norm_diff_threshold","description":"Norm Diff Threshold","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"max_z_diff","description":"Max Z Diff","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"classes","description":"Classes","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"ground","description":"Ground","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"lidar_segmentation_based_filter","display_name":"Lidar Segmentation Based Filter","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"norm_diff_threshold","description":"Norm Diff Threshold","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"max_z_diff","description":"Max Z Diff","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"classify_points","description":"Classify Points","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"modify_lidar","display_name":"Modify Lidar","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"statement","description":"Statement","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"normalize_lidar","display_name":"Normalize Lidar","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"dtm","description":"Dtm","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"no_negatives","description":"No Negatives","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"remove_duplicates","display_name":"Remove Duplicates","summary":"","category":"LiDAR - Filtering & Classification","taxonomy_category":"lidar","taxonomy_subcategory":"filtering_classification","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"include_z","description":"Include Z","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"flightline_overlap","display_name":"Flightline Overlap","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_block_maximum","display_name":"Lidar Block Maximum","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"interpolation_parameter","description":"Interpolation Parameter","type":"Literal[\"elevation\", \"intensity\", \"class\", \"return_number\", \"number_of_returns\", \"scan_angle\", \"time\", \"rgb\", \"user_data\"]","required":false,"default":"elevation","options":[],"kind":"int"},{"name":"returns_included","description":"Returns Included","type":"Literal[\"all\", \"first\", \"last\"]","required":false,"default":"all","options":[],"kind":"string"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"min_elev","description":"Min Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_block_minimum","display_name":"Lidar Block Minimum","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"interpolation_parameter","description":"Interpolation Parameter","type":"Literal[\"elevation\", \"intensity\", \"class\", \"return_number\", \"number_of_returns\", \"scan_angle\", \"time\", \"rgb\", \"user_data\"]","required":false,"default":"elevation","options":[],"kind":"int"},{"name":"returns_included","description":"Returns Included","type":"Literal[\"all\", \"first\", \"last\"]","required":false,"default":"all","options":[],"kind":"string"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"min_elev","description":"Min Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_construct_vector_tin","display_name":"Lidar Construct Vector Tin","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"returns_included","description":"Returns Included","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"min_elev","description":"Min Elev","type":"float","required":false,"default":-1.7976931348623157e+308,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float","required":false,"default":1.7976931348623157e+308,"options":[],"kind":"double"},{"name":"max_triangle_edge_length","description":"Max Triangle Edge Length","type":"float","required":false,"default":1.7976931348623157e+308,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"lidar_contour","display_name":"Lidar Contour","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"contour_interval","description":"Contour Interval","type":"float","required":false,"default":10,"options":[],"kind":"double"},{"name":"base_contour","description":"Base Contour","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"smooth","description":"Smooth","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"interpolation_parameter","description":"Interpolation Parameter","type":"str","required":false,"default":"elevation","options":[],"kind":"string"},{"name":"returns_included","description":"Returns Included","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"min_elev","description":"Min Elev","type":"float","required":false,"default":-1.7976931348623157e+308,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float","required":false,"default":1.7976931348623157e+308,"options":[],"kind":"double"},{"name":"max_triangle_edge_length","description":"Max Triangle Edge Length","type":"float","required":false,"default":1.7976931348623157e+308,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"lidar_digital_surface_model","display_name":"Lidar Digital Surface Model","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"min_elev","description":"Min Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_triangle_edge_length","description":"Max Triangle Edge Length","type":"float","required":false,"default":1.7976931348623157e+308,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_hex_bin","display_name":"Lidar Hex Bin","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"width","description":"Width","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"orientation","description":"Orientation","type":"str","required":false,"default":"h","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"lidar_hillshade","display_name":"Lidar Hillshade","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":-1,"options":[],"kind":"double"},{"name":"azimuth","description":"Azimuth","type":"float","required":false,"default":315,"options":[],"kind":"double"},{"name":"altitude","description":"Altitude","type":"float","required":false,"default":30,"options":[],"kind":"double"},{"name":"returns_included","description":"Returns Included","type":"Literal[\"all\", \"first\", \"last\"]","required":false,"default":"all","options":[],"kind":"string"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"min_elev","description":"Min Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_idw_interpolation","display_name":"Lidar Idw Interpolation","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"weight","description":"Weight","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":2.5,"options":[],"kind":"double"},{"name":"interpolation_parameter","description":"Interpolation Parameter","type":"Literal[\"elevation\", \"intensity\", \"class\", \"return_number\", \"number_of_returns\", \"scan_angle\", \"time\", \"rgb\", \"user_data\"]","required":false,"default":"elevation","options":[],"kind":"int"},{"name":"returns_included","description":"Returns Included","type":"Literal[\"all\", \"first\", \"last\"]","required":false,"default":"all","options":[],"kind":"string"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"min_elev","description":"Min Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"min_points","description":"Min Points","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_nearest_neighbour_gridding","display_name":"Lidar Nearest Neighbour Gridding","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":2.5,"options":[],"kind":"double"},{"name":"interpolation_parameter","description":"Interpolation Parameter","type":"Literal[\"elevation\", \"intensity\", \"class\", \"return_number\", \"number_of_returns\", \"scan_angle\", \"time\", \"rgb\", \"user_data\"]","required":false,"default":"elevation","options":[],"kind":"int"},{"name":"returns_included","description":"Returns Included","type":"Literal[\"all\", \"first\", \"last\"]","required":false,"default":"all","options":[],"kind":"string"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"min_elev","description":"Min Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_radial_basis_function_interpolation","display_name":"Lidar Radial Basis Function Interpolation","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"num_points","description":"Num Points","type":"int","required":false,"default":15,"options":[],"kind":"int"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"func_type","description":"Func Type","type":"Literal[\"thinplatespline\", \"polyharmonic\", \"gaussian\", \"multiquadric\", \"inversemultiquadric\"]","required":false,"default":"thinplatespline","options":[],"kind":"string"},{"name":"poly_order","description":"Poly Order","type":"Literal[\"none\", \"constant\", \"quadratic\"]","required":false,"default":"none","options":[],"kind":"string"},{"name":"weight","description":"Weight","type":"float","required":false,"default":0.1,"options":[],"kind":"double"},{"name":"interpolation_parameter","description":"Interpolation Parameter","type":"Literal[\"elevation\", \"intensity\", \"class\", \"return_number\", \"number_of_returns\", \"scan_angle\", \"time\", \"rgb\", \"user_data\"]","required":false,"default":"elevation","options":[],"kind":"int"},{"name":"returns_included","description":"Returns Included","type":"Literal[\"all\", \"first\", \"last\"]","required":false,"default":"all","options":[],"kind":"string"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"min_elev","description":"Min Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_sibson_interpolation","display_name":"Lidar Sibson Interpolation","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"interpolation_parameter","description":"Interpolation Parameter","type":"Literal[\"elevation\", \"intensity\", \"class\", \"return_number\", \"number_of_returns\", \"scan_angle\", \"time\", \"rgb\", \"user_data\"]","required":false,"default":"elevation","options":[],"kind":"int"},{"name":"returns_included","description":"Returns Included","type":"Literal[\"all\", \"first\", \"last\"]","required":false,"default":"all","options":[],"kind":"string"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"min_elev","description":"Min Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_thin","display_name":"Lidar Thin","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"method","description":"Method","type":"str","required":false,"default":"first","options":[],"kind":"string"},{"name":"save_filtered","description":"Save Filtered","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"},{"name":"filtered_output","description":"Filtered Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Lidar"},{"id":"lidar_thin_high_density","display_name":"Lidar Thin High Density","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"density","description":"Density","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"save_filtered","description":"Save Filtered","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"},{"name":"filtered_output","description":"Filtered Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Lidar"},{"id":"lidar_tile_footprint","display_name":"Lidar Tile Footprint","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"output_hulls","description":"Output Hulls","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"lidar_tin_gridding","display_name":"Lidar Tin Gridding","summary":"","category":"LiDAR - Interpolation & Gridding","taxonomy_category":"lidar","taxonomy_subcategory":"interpolation_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"max_triangle_edge_length","description":"Max Triangle Edge Length","type":"float","required":false,"default":1.7976931348623157e+308,"options":[],"kind":"double"},{"name":"interpolation_parameter","description":"Interpolation Parameter","type":"Literal[\"elevation\", \"intensity\", \"class\", \"return_number\", \"number_of_returns\", \"scan_angle\", \"time\", \"rgb\", \"user_data\"]","required":false,"default":"elevation","options":[],"kind":"int"},{"name":"returns_included","description":"Returns Included","type":"Literal[\"all\", \"first\", \"last\"]","required":false,"default":"all","options":[],"kind":"string"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"min_elev","description":"Min Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"triangulation_backend","description":"Triangulation Backend","type":"Literal[\"auto\", \"delaunator\", \"wbtopology\"]","required":false,"default":"auto","options":[],"kind":"string"},{"name":"triangulation_auto_threshold","description":"Triangulation Auto Threshold","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"triangulation_epsilon","description":"Triangulation Epsilon","type":"float","required":false,"default":1e-12,"options":[],"kind":"double"},{"name":"triangulation_thin_cell_size","description":"Triangulation Thin Cell Size","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"triangulation_thin_method","description":"Triangulation Thin Method","type":"Literal[\"nearest_center\", \"min_value\", \"max_value\"]","required":false,"default":"nearest_center","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"colourize_based_on_class","display_name":"Colourize Based On Class","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"intensity_blending_amount","description":"Intensity Blending Amount","type":"float","required":false,"default":50,"options":[],"kind":"double"},{"name":"clr_str","description":"Clr Str","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"use_unique_clrs_for_buildings","description":"Use Unique Clrs For Buildings","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"colourize_based_on_point_returns","display_name":"Colourize Based On Point Returns","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"intensity_blending_amount","description":"Intensity Blending Amount","type":"float","required":false,"default":50,"options":[],"kind":"double"},{"name":"only_ret_colour","description":"Only Ret Colour","type":"str","required":false,"default":"(230,214,170)","options":[],"kind":"string"},{"name":"first_ret_colour","description":"First Ret Colour","type":"str","required":false,"default":"(0,140,0)","options":[],"kind":"string"},{"name":"intermediate_ret_colour","description":"Intermediate Ret Colour","type":"str","required":false,"default":"(255,0,255)","options":[],"kind":"string"},{"name":"last_ret_colour","description":"Last Ret Colour","type":"str","required":false,"default":"(0,0,255)","options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"find_flightline_edge_points","display_name":"Find Flightline Edge Points","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"individual_tree_detection","display_name":"Individual Tree Detection","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"min_search_radius","description":"Min Search Radius","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"min_height","description":"Min Height","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"max_search_radius","description":"Max Search Radius","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_height","description":"Max Height","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"only_use_veg","description":"Only Use Veg","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"lidar_eigenvalue_features","display_name":"Lidar Eigenvalue Features","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"num_neighbours","description":"Num Neighbours","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"search_radius","description":"Search Radius","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"lidar_histogram","display_name":"Lidar Histogram","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"parameter","description":"Parameter","type":"str","required":false,"default":"elevation","options":[],"kind":"string"},{"name":"clip_percent","description":"Clip Percent","type":"float","required":false,"default":1,"options":[],"kind":"double"}],"return_type":"str"},{"id":"lidar_info","display_name":"Lidar Info","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"show_point_density","description":"Show Point Density","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"show_vlrs","description":"Show Vlrs","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"show_geokeys","description":"Show Geokeys","type":"bool","required":false,"default":true,"options":[],"kind":"bool"}],"return_type":"str"},{"id":"lidar_kappa","display_name":"Lidar Kappa","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"classification_lidar","description":"Classification Lidar","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"reference_lidar","description":"Reference Lidar","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"report_path","description":"Report Path","type":"str","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output_class_accuracy","description":"Output Class Accuracy","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_point_density","display_name":"Lidar Point Density","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":2.5,"options":[],"kind":"double"},{"name":"returns_included","description":"Returns Included","type":"Literal[\"all\", \"first\", \"last\"]","required":false,"default":"all","options":[],"kind":"string"},{"name":"excluded_classes","description":"Excluded Classes","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"min_elev","description":"Min Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_elev","description":"Max Elev","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lidar_point_return_analysis","display_name":"Lidar Point Return Analysis","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"create_output","description":"Create Output","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"report_path","description":"Report Path","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"lidar_point_stats","display_name":"Lidar Point Stats","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str | None","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"num_points","description":"Num Points","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"num_pulses","description":"Num Pulses","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"avg_points_per_pulse","description":"Avg Points Per Pulse","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"z_range","description":"Z Range","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"intensity_range","description":"Intensity Range","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"predominant_class","description":"Predominant Class","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output_directory","description":"Output Directory","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"lidar_ransac_planes","display_name":"Lidar Ransac Planes","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"num_iterations","description":"Num Iterations","type":"int","required":false,"default":50,"options":[],"kind":"int"},{"name":"num_samples","description":"Num Samples","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"inlier_threshold","description":"Inlier Threshold","type":"float","required":false,"default":0.15,"options":[],"kind":"double"},{"name":"acceptable_model_size","description":"Acceptable Model Size","type":"int","required":false,"default":30,"options":[],"kind":"int"},{"name":"max_planar_slope","description":"Max Planar Slope","type":"float","required":false,"default":75,"options":[],"kind":"double"},{"name":"classify","description":"Classify","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"only_last_returns","description":"Only Last Returns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"lidar_rooftop_analysis","display_name":"Lidar Rooftop Analysis","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"lidar_inputs","description":"Lidar Inputs","type":"list[Lidar | str]","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"building_footprints","description":"Building Footprints","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"num_iterations","description":"Num Iterations","type":"int","required":false,"default":50,"options":[],"kind":"int"},{"name":"num_samples","description":"Num Samples","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"inlier_threshold","description":"Inlier Threshold","type":"float","required":false,"default":0.15,"options":[],"kind":"double"},{"name":"acceptable_model_size","description":"Acceptable Model Size","type":"int","required":false,"default":30,"options":[],"kind":"int"},{"name":"max_planar_slope","description":"Max Planar Slope","type":"float","required":false,"default":75,"options":[],"kind":"double"},{"name":"norm_diff_threshold","description":"Norm Diff Threshold","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"azimuth","description":"Azimuth","type":"float","required":false,"default":180,"options":[],"kind":"double"},{"name":"altitude","description":"Altitude","type":"float","required":false,"default":30,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"normal_vectors","display_name":"Normal Vectors","summary":"","category":"LiDAR - Analysis Metrics","taxonomy_category":"lidar","taxonomy_subcategory":"analysis_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Lidar | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"search_radius","description":"Search Radius","type":"float","required":false,"default":-1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"lidar_out"}],"return_type":"Lidar"},{"id":"lidar_qa_and_confidence","display_name":"Lidar Qa And Confidence","summary":"","category":"LiDAR - Workflow Products","taxonomy_category":"lidar","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"input","description":"Input LiDAR path.","type":"lidar","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"qa_mode","description":"QA mode: strict, balanced, permissive, auto.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"block_size","description":"Grid cell size in xy units.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"max_building_size","description":"Maximum expected object width in xy units.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"slope_threshold","description":"Minimum edge slope in degrees.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"elev_threshold","description":"Reference surface threshold in z units.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"high_confidence_threshold","description":"Threshold used for high-confidence fraction metric.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"adapt_by_slope","description":"Enable slope-adaptive tolerance scaling.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"adapt_by_roughness","description":"Enable roughness-adaptive tolerance scaling.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"density_window","description":"Odd window size for local confidence-density proxy.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"roughness_window","description":"Odd window size for local roughness proxy.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"hotspot_min_cells","description":"Minimum connected hotspot size in raster cells.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"hotspot_rank_top_n","description":"Optional maximum number of ranked hotspots to include.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"checkpoints","description":"Optional checkpoint point layer for vertical validation.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"checkpoint_z_field","description":"Optional checkpoint elevation field override.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"fast_mode","description":"Enable fast mode for quick exploratory runs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Prefix for QA raster/report artifacts.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Optional output LiDAR path.","type":"file","required":false,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"lidar_terrain_product_suite","display_name":"Lidar Terrain Product Suite","summary":"","category":"LiDAR - Workflow Products","taxonomy_category":"lidar","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"input","description":"Input LiDAR path (LAS/LAZ).","type":"lidar","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"profile","description":"QA profile: strict, balanced, permissive.","type":"file","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"block_size","description":"Grid cell size in map units.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"max_building_size","description":"Maximum expected object width in map units.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"slope_threshold","description":"Minimum edge slope in degrees for off-terrain removal.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"elev_threshold","description":"Reference surface threshold in z units.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"z_factor","description":"Vertical exaggeration factor for slope/hillshade.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"hillshade_azimuth","description":"Sun azimuth in degrees for hillshade.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"hillshade_altitude","description":"Sun altitude in degrees for hillshade.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"high_confidence_threshold","description":"Threshold for high-confidence fraction metric.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"output_prefix","description":"Prefix for all output artifacts.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Optional output LiDAR path.","type":"file","required":false,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"lidar_change_and_disturbance_analysis","display_name":"Lidar Change And Disturbance Analysis","summary":"","category":"LiDAR - Workflow Products","taxonomy_category":"lidar","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"baseline_tiles","description":"Baseline Tiles","type":"list[str] | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"monitor_tiles","description":"Monitor Tiles","type":"list[str] | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"reference_perimeters","description":"Reference Perimeters","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"attribution_buffer_meters","description":"Attribution Buffer Meters","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"min_overlap_fraction","description":"Min Overlap Fraction","type":"float","required":false,"default":0.1,"options":[],"kind":"double"},{"name":"resolution","description":"Resolution","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"min_change_m","description":"Min Change M","type":"float","required":false,"default":0.25,"options":[],"kind":"double"},{"name":"output_prefix","description":"Output Prefix","type":"str","required":true,"default":"lidar_change","options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"sidewalk_vegetation_accessibility_monitoring","display_name":"Sidewalk Vegetation Accessibility Monitoring","summary":"","category":"LiDAR - Workflow Products","taxonomy_category":"lidar","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"lidar_tiles","description":"Array of LiDAR tile paths or a directory containing LAS/LAZ tiles.","type":"lidar","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"sidewalks","description":"Input sidewalk line or polygon layer.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"sidewalks_epsg","description":"Optional EPSG override when the sidewalk layer lacks CRS metadata.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"resolution","description":"Intermediate raster resolution in map units.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"segment_length_m","description":"Optional fixed segment length for line inputs. Ignored for polygon inputs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"clearance_height_m","description":"Vegetation height threshold considered obstructive.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"buffer_distance_m","description":"Neighborhood radius around sidewalk geometry sampled for obstruction.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_tile_workers","description":"Optional max number of parallel tile workers (0 = auto).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for GeoPackage and JSON summary artifacts.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"snap_events_to_routes","display_name":"Snap Events To Routes","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"routes","description":"Input route line layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"events","description":"Input event point layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"max_offset_distance","description":"Optional maximum route offset distance in map units.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output snapped event point vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"route_event_governance_for_linear_assets","display_name":"Route Event Governance For Linear Assets","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"events","description":"Input event vector layer (GeoPackage, GeoJSON, or Shapefile).","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"route_id_field","description":"Field name containing route identifiers.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"from_measure_field","description":"Field name for interval start measure.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"to_measure_field","description":"Field name for interval end measure.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"gap_tolerance","description":"Measure gap smaller than this value is ignored (default 0.0).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"overlap_tolerance","description":"Measure overlap smaller than this value is ignored (default 0.0).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"auto_fix","description":"Automatically correct descending intervals and overlapping events (default false).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"domain_rules_json","description":"Optional JSON file path describing per-field validation rules (allowed_values, regex, min, max).","type":"json","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"governed_events","description":"Output vector path for QA-passed events.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"issues_csv","description":"Output CSV path for per-event issue log.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"corrected_events","description":"Output vector path for auto-corrected events (only written when auto_fix=true).","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"governance_report","description":"Output JSON path for governance summary report.","type":"json","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"remediation_queue_csv","description":"Optional output CSV path with prioritized corrective actions by issue.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"html_report","description":"Optional output path for an HTML governance summary report.","type":"text","required":false,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"locate_points_along_routes","display_name":"Locate Points Along Routes","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"routes","description":"Routes","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"max_offset_distance","description":"Max Offset Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"points_along_lines","display_name":"Points Along Lines","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"spacing","description":"Spacing","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"include_end","description":"Include End","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"route_calibrate","display_name":"Route Calibrate","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"routes","description":"Input route line layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"control_points","description":"Input control-point layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"control_measure_field","description":"Control-point field containing known measure values.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"route_id_field","description":"Optional route identifier field in routes. Defaults to feature FID.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"control_route_id_field","description":"Optional route identifier field in control points. Defaults to feature FID.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"from_measure_field","description":"Output field for route start measure (default 'from_measure').","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"to_measure_field","description":"Output field for route end measure (default 'to_measure').","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"snap_tolerance","description":"Maximum control-point offset distance from route geometry (default 1.0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output calibrated route layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"route_event_lines_from_layer","display_name":"Route Event Lines From Layer","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"routes","description":"Routes","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"events","description":"Events","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"event_route_field","description":"Event Route Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"from_measure_field","description":"From Measure Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"to_measure_field","description":"To Measure Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"route_id_field","description":"Route Id Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"write_event_fid","description":"Write Event Fid","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"write_event_xy","description":"Write Event Xy","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"route_event_lines_from_table","display_name":"Route Event Lines From Table","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"routes","description":"Routes","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"events","description":"Events","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"event_route_field","description":"Event Route Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"from_measure_field","description":"From Measure Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"to_measure_field","description":"To Measure Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"route_id_field","description":"Route Id Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"route_event_merge","display_name":"Route Event Merge","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"events","description":"Input event layer containing route intervals.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"event_route_field","description":"Event-layer route identifier field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"from_measure_field","description":"Event-layer interval start field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"to_measure_field","description":"Event-layer interval end field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"group_fields","description":"Optional comma-delimited fields used for merge compatibility. Defaults to all non-measure fields.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"gap_tolerance","description":"Maximum gap allowed for adjacency merge (default 0.0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"conflict_mode","description":"Overlap handling mode: error|skip (default error).","type":"string","required":false,"default":null,"options":["error","skip"],"kind":"enum"},{"name":"output","description":"Output merged-event layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"route_event_overlay","display_name":"Route Event Overlay","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"primary_events","description":"Primary event layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"overlay_events","description":"Overlay event layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"primary_route_field","description":"Primary layer route identifier field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"primary_from_measure_field","description":"Primary layer interval start field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"primary_to_measure_field","description":"Primary layer interval end field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"overlay_route_field","description":"Overlay layer route identifier field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"overlay_from_measure_field","description":"Overlay layer interval start field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"overlay_to_measure_field","description":"Overlay layer interval end field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"min_overlap_length","description":"Minimum overlap length to keep (default 0.0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output overlay layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"route_event_points_from_layer","display_name":"Route Event Points From Layer","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"routes","description":"Routes","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"events","description":"Events","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"event_route_field","description":"Event Route Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"measure_field","description":"Measure Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"route_id_field","description":"Route Id Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"write_event_fid","description":"Write Event Fid","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"write_event_xy","description":"Write Event Xy","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"route_event_points_from_table","display_name":"Route Event Points From Table","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"routes","description":"Routes","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"events","description":"Events","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"event_route_field","description":"Event Route Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"measure_field","description":"Measure Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"route_id_field","description":"Route Id Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"route_event_split","display_name":"Route Event Split","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"events","description":"Input event layer containing route intervals.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"boundaries","description":"Input boundary layer containing route measure breakpoints.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"event_route_field","description":"Event-layer route identifier field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"from_measure_field","description":"Event-layer interval start field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"to_measure_field","description":"Event-layer interval end field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"boundary_route_field","description":"Boundary-layer route identifier field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"boundary_measure_field","description":"Boundary-layer measure field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"min_segment_length","description":"Optional minimum split segment length to keep (default 0.0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output split-event layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"route_measure_qa","display_name":"Route Measure Qa","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"events","description":"Input event layer containing route intervals.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"route_field","description":"Route identifier field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"from_measure_field","description":"Interval start field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"to_measure_field","description":"Interval end field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"gap_tolerance","description":"Gap tolerance (default 0.0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"overlap_tolerance","description":"Overlap tolerance (default 0.0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output QA diagnostics layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"route_recalibrate","display_name":"Route Recalibrate","summary":"","category":"Vector - Linear Referencing","taxonomy_category":"vector","taxonomy_subcategory":"linear_referencing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"original_routes","description":"Reference routes containing prior calibrated measures.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"edited_routes","description":"Edited routes to recalibrate.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"route_id_field","description":"Optional shared route identifier field. Defaults to feature FID.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"from_measure_field","description":"Measure-start field name (default 'from_measure').","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"to_measure_field","description":"Measure-end field name (default 'to_measure').","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output recalibrated route layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"build_network_topology","display_name":"Build Network Topology","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input line network layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Optional snapping tolerance used while detecting and splitting intersections.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output topologically noded edge vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"nodes_output","description":"Optional output node vector path.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"closest_facility_network","display_name":"Closest Facility Network","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"incidents","description":"Incidents","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"facilities","description":"Facilities","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Max Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"edge_cost_field","description":"Edge Cost Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"One Way Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Blocked Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"barriers","description":"Barriers","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"barrier_snap_distance","description":"Barrier Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"turn_penalty","description":"Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"u_turn_penalty","description":"U Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"forbid_u_turns","description":"Forbid U Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_left_turns","description":"Forbid Left Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_right_turns","description":"Forbid Right Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"turn_restrictions_csv","description":"Turn Restrictions Csv","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_cost_profile","description":"Temporal Cost Profile","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Temporal Edge Id Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Departure Time","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Temporal Mode","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_fallback","description":"Temporal Fallback","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_profile_report","description":"Temporal Profile Report","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"emergency_scenario_routing_and_accessibility_simulator","display_name":"Emergency Scenario Routing And Accessibility Simulator","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"network","description":"Input line network layer.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"critical_facilities","description":"Critical facility points (e.g., hospitals/fire/EMS).","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"demand_points","description":"Optional demand points for coverage KPI computation.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"ring_costs","description":"Required ring costs array, e.g. [5,10,15].","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"scenario_csv","description":"Scenario CSV with columns: scenario_id,max_cost_multiplier[,blocked_value].","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"scenario_template","description":"Optional scenario authoring template: custom|flood|wildfire|earthquake. Applies template-specific validation guardrails.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"scenario_block_source_field","description":"Optional network field used to derive scenario-specific blocked edges from blocked_value.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"baseline_service_areas","description":"Output baseline merged service-area polygons.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"worst_case_service_areas","description":"Output worst-scenario service-area polygons.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"scenario_summary_csv","description":"Output scenario KPI CSV with baseline deltas.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"simulation_report","description":"Output JSON simulation report.","type":"json","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"html_report","description":"Optional output path for an HTML accessibility simulation summary report.","type":"text","required":false,"default":null,"options":[],"kind":"file_out"},{"name":"cost_method","description":"Cost method: distance, mode_speed, edge_field, or temporal.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"edge_cost_field","description":"Optional numeric line field used as an impedance multiplier for segment length.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"Optional line field marking one-way digitized edges (supports FT/TF/B plus legacy boolean values).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"mode_field","description":"Optional line attribute field identifying travel mode per segment; enables mode-aware service-area costs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"default_mode_speed","description":"Default mode speed in coordinate-units per time unit when mode_field is provided (default: 1).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"mode_speed_overrides","description":"Optional comma-separated mode:speed overrides (e.g., walk:1.4,drive:12).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"allowed_modes","description":"Optional comma-separated allow-list of modes to include when mode_field is provided.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_cost_profile","description":"Optional CSV defining time-dependent edge costs (columns: edge_id,dow,start_minute,end_minute,value).","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Optional network field used to match temporal_cost_profile edge_id values (default EDGE_ID).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Optional RFC3339 departure time used for temporal profile lookup.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Optional temporal interpretation mode: multiplier or absolute.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_fallback","description":"Optional fallback when temporal row is missing: static_cost or error.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"turn_penalty","description":"Optional additive cost applied to non-straight turns at network nodes.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"u_turn_penalty","description":"Optional additive cost applied to U-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"forbid_u_turns","description":"If true, disallow U-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"forbid_left_turns","description":"If true, disallow left-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"forbid_right_turns","description":"If true, disallow right-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"turn_restrictions_csv","description":"Optional CSV of turn transitions using columns prev_x,prev_y,node_x,node_y,next_x,next_y. Optional columns: forbidden and turn_cost.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"node_cost_points","description":"Optional point layer providing per-node entry costs (e.g., intersection delay).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"node_cost_field","description":"Numeric field in node_cost_points containing non-negative per-node costs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"node_cost_snap_distance","description":"Optional max distance from each node-cost point to a network node for assignment.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"fleet_routing_and_dispatch_optimizer","display_name":"Fleet Routing And Dispatch Optimizer","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"network","description":"Street network (LineVector).","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"depots","description":"Depot locations (PointVector).","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"stops","description":"Stop locations (PointVector).","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"vehicles_csv","description":"Vehicle specifications CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"routes_output","description":"Output routes vector.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"assignment_csv_output","description":"Output assignment CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"route_kpis_csv_output","description":"Output KPIs CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"exceptions_csv_output","description":"Output exceptions CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"objective","description":"Optimization objective (default: minimize_distance).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"restrictions","description":"Optional CSV with barriers/turn restrictions.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"edge_cost_field","description":"Optional numeric line field used as an impedance multiplier for segment length.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"Optional line field marking one-way digitized edges (supports FT/TF/B plus legacy boolean values).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"network_speed_kmph","description":"Optional network travel speed used to convert path distance into travel time (default: 80).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"html_report","description":"Optional output path for an HTML fleet routing dispatch summary report.","type":"text","required":false,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"generate_network_nodes","display_name":"Generate Network Nodes","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input line network layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Optional node snapping tolerance for graph construction.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output node point vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"k_shortest_paths_network","display_name":"K Shortest Paths Network","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"start_x","description":"Start X","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"start_y","description":"Start Y","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"end_x","description":"End X","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"end_y","description":"End Y","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"k","description":"K","type":"int","required":true,"default":null,"options":[],"kind":"int"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Max Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"edge_cost_field","description":"Edge Cost Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"One Way Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Blocked Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"barriers","description":"Barriers","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"barrier_snap_distance","description":"Barrier Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"turn_penalty","description":"Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"u_turn_penalty","description":"U Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"forbid_u_turns","description":"Forbid U Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_left_turns","description":"Forbid Left Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_right_turns","description":"Forbid Right Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"turn_restrictions_csv","description":"Turn Restrictions Csv","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_cost_profile","description":"Temporal Cost Profile","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Temporal Edge Id Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Departure Time","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Temporal Mode","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_fallback","description":"Temporal Fallback","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_profile_report","description":"Temporal Profile Report","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"location_allocation_network","display_name":"Location Allocation Network","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"demand_points","description":"Demand Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"facilities","description":"Facilities","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"facility_count","description":"Facility Count","type":"int","required":true,"default":null,"options":[],"kind":"int"},{"name":"solver_mode","description":"Solver Mode","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"demand_weight_field","description":"Demand Weight Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"facility_capacity_field","description":"Facility Capacity Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"required_facility_field","description":"Required Facility Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"forbidden_facility_field","description":"Forbidden Facility Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Max Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"edge_cost_field","description":"Edge Cost Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"One Way Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Blocked Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"barriers","description":"Barriers","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"barrier_snap_distance","description":"Barrier Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"turn_penalty","description":"Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"u_turn_penalty","description":"U Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"forbid_u_turns","description":"Forbid U Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_left_turns","description":"Forbid Left Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_right_turns","description":"Forbid Right Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"turn_restrictions_csv","description":"Turn Restrictions Csv","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_cost_profile","description":"Temporal Cost Profile","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Temporal Edge Id Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Departure Time","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Temporal Mode","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_fallback","description":"Temporal Fallback","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_profile_report","description":"Temporal Profile Report","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"map_matching_v1","display_name":"Map Matching V1","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"trajectory_points","description":"Trajectory Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"timestamp_field","description":"Timestamp Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"search_radius","description":"Search Radius","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"candidate_k","description":"Candidate K","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Max Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"edge_cost_field","description":"Edge Cost Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"One Way Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Blocked Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"barriers","description":"Barriers","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"barrier_snap_distance","description":"Barrier Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"turn_penalty","description":"Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"u_turn_penalty","description":"U Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"forbid_u_turns","description":"Forbid U Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_left_turns","description":"Forbid Left Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_right_turns","description":"Forbid Right Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"turn_restrictions_csv","description":"Turn Restrictions Csv","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"matched_points_output","description":"Matched Points Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"match_report","description":"Match Report","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"market_access_and_site_intelligence_workflow","display_name":"Market Access And Site Intelligence Workflow","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"network","description":"Input line network.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"sites_existing","description":"Incumbent baseline sites used for existing coverage and coverage-gain calculations.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"sites_candidates","description":"Candidate expansion sites (required for user_supplied mode).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"demand_surface","description":"Demand locations/centroids.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"competition_sites","description":"Optional separate competitor layer used for overlap pressure; if omitted, overlap falls back to sites_existing.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"ring_costs","description":"Drive-time costs for catchments.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"candidate_mode","description":"Candidate mode: user_supplied or generated.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"num_sites_to_select","description":"Number of top sites to select.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"selection_strategy","description":"Selection strategy: single_best or top_k_greedy.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"min_new_site_separation","description":"Minimum separation between selected sites in map units.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_generated_candidates","description":"Maximum generated candidate points.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"catchments_output","description":"Output candidate-level catchment polygons (convex-hull trade areas with fallback square polygons when hulls cannot be formed).","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"overlap_analysis_output","description":"Output overlap analysis.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"candidate_rank_csv","description":"Output candidate ranking CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"executive_summary_json","description":"Output executive summary.","type":"json","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"market_action_queue_csv","description":"Optional prioritized expansion action queue CSV.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"html_report","description":"Optional output path for an HTML market access and site intelligence summary report.","type":"text","required":false,"default":null,"options":[],"kind":"file_out"},{"name":"cost_method","description":"Cost method: distance (default), mode_speed, edge_field, or temporal.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"edge_cost_field","description":"Optional numeric line field used as an impedance multiplier for segment length.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"Optional line field marking one-way digitized edges (supports FT/TF/B plus legacy boolean values).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"mode_field","description":"Optional line attribute field identifying travel mode per segment.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"default_mode_speed","description":"Default mode speed when mode_field is provided.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"mode_speed_overrides","description":"Optional comma-separated mode:speed overrides.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"allowed_modes","description":"Optional comma-separated allow-list of modes.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_cost_profile","description":"Optional CSV defining time-dependent edge costs.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Optional network field used to match temporal profile edge IDs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Optional RFC3339 departure time used for temporal profile lookup.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Optional temporal interpretation mode: multiplier or absolute.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_fallback","description":"Optional fallback when temporal row is missing: static_cost or error.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"turn_penalty","description":"Optional additive cost applied to non-straight turns at network nodes.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"u_turn_penalty","description":"Optional additive cost applied to U-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"forbid_u_turns","description":"If true, disallow U-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"forbid_left_turns","description":"If true, disallow left-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"forbid_right_turns","description":"If true, disallow right-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"turn_restrictions_csv","description":"Optional CSV of turn transitions with optional per-turn costs.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"node_cost_points","description":"Optional point layer providing per-node entry costs (e.g., intersection delay).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"node_cost_field","description":"Numeric field in node_cost_points containing non-negative per-node costs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"node_cost_snap_distance","description":"Optional max distance from each node-cost point to a network node for assignment.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"multimodal_od_cost_matrix","display_name":"Multimodal Od Cost Matrix","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input line network layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"origins","description":"Origin point layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"destinations","description":"Destination point layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"mode_field","description":"Line attribute field that identifies travel mode per segment.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"snap_tolerance","description":"Optional node snapping tolerance for graph construction.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Optional max distance from origin/destination points to nearest network node.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"default_mode_speed","description":"Default mode speed in coordinate-units per time unit (default: 1).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"mode_speed_overrides","description":"Optional comma-separated mode:speed overrides (for example: walk:1.4,drive:12,transit:8).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"allowed_modes","description":"Optional comma-separated allow-list of modes to include in routing.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"transfer_penalty","description":"Optional additive penalty applied each time the route changes mode.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"temporal_cost_profile","description":"Optional CSV defining time-dependent edge costs (columns: edge_id,dow,start_minute,end_minute,value).","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Optional network field used to match temporal_cost_profile edge_id values (default EDGE_ID).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Optional RFC3339 departure time used for temporal profile lookup.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Optional temporal interpretation mode: multiplier or absolute.","type":"string","required":false,"default":null,"options":["multiplier","absolute"],"kind":"enum"},{"name":"temporal_fallback","description":"Optional fallback when temporal row is missing: static_cost or error.","type":"string","required":false,"default":null,"options":["static_cost","error"],"kind":"enum"},{"name":"temporal_profile_report","description":"Optional JSON output path for temporal profile diagnostics when using direct temporal input.","type":"json","required":false,"default":null,"options":[],"kind":"file_out"},{"name":"scenario_bundle_csv","description":"Optional CSV listing named temporal scenarios for comparative multi-scenario OD output.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output","description":"Output CSV path.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"multimodal_routes_from_od","display_name":"Multimodal Routes From Od","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input line network layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"origins","description":"Origin point layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"destinations","description":"Destination point layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"mode_field","description":"Line attribute field that identifies travel mode per segment.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"snap_tolerance","description":"Optional node snapping tolerance for graph construction.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Optional max distance from origin/destination points to nearest network node.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"default_mode_speed","description":"Default mode speed in coordinate-units per time unit (default: 1).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"mode_speed_overrides","description":"Optional comma-separated mode:speed overrides (for example: walk:1.4,drive:12,transit:8).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"allowed_modes","description":"Optional comma-separated allow-list of modes to include in routing.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"transfer_penalty","description":"Optional additive penalty applied each time the route changes mode.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"temporal_cost_profile","description":"Optional CSV defining time-dependent edge costs (columns: edge_id,dow,start_minute,end_minute,value).","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Optional network field used to match temporal_cost_profile edge_id values (default EDGE_ID).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Optional RFC3339 departure time used for temporal profile lookup.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Optional temporal interpretation mode: multiplier or absolute.","type":"string","required":false,"default":null,"options":["multiplier","absolute"],"kind":"enum"},{"name":"temporal_fallback","description":"Optional fallback when temporal row is missing: static_cost or error.","type":"string","required":false,"default":null,"options":["static_cost","error"],"kind":"enum"},{"name":"temporal_profile_report","description":"Optional JSON output path for temporal profile diagnostics when using direct temporal input.","type":"json","required":false,"default":null,"options":[],"kind":"file_out"},{"name":"scenario_bundle_csv","description":"Optional CSV listing named temporal scenarios for comparative multi-scenario route output.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output","description":"Output route line vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"multimodal_shortest_path","display_name":"Multimodal Shortest Path","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input line network layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"start_x","description":"Start x coordinate.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"start_y","description":"Start y coordinate.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"end_x","description":"End x coordinate.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"end_y","description":"End y coordinate.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"mode_field","description":"Line attribute field that identifies travel mode per segment.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"snap_tolerance","description":"Optional node snapping tolerance for graph construction.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Optional max distance from start/end coordinates to nearest network node.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"default_mode_speed","description":"Default mode speed in coordinate-units per time unit (default: 1).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"mode_speed_overrides","description":"Optional comma-separated mode:speed overrides (for example: walk:1.4,drive:12,transit:8).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"allowed_modes","description":"Optional comma-separated allow-list of modes to include in routing.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"transfer_penalty","description":"Optional additive penalty applied each time the route changes mode.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output line vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"network_accessibility_metrics","display_name":"Network Accessibility Metrics","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input line network layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"origins","description":"Origin point layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"destinations","description":"Destination point layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Optional node snapping tolerance for graph construction.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Optional max distance from origin/destination points to nearest network node.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"impedance_cutoff","description":"Optional maximum distance threshold for counting reachable destinations (default: infinite).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"decay_function","description":"Optional decay function: 'none' (default), 'linear', or 'exponential' for distance-weighted accessibility.","type":"string","required":false,"default":null,"options":["none","linear","exponential"],"kind":"enum"},{"name":"decay_parameter","description":"Optional decay parameter (lambda for exponential, rate for linear).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"edge_cost_field","description":"Optional numeric line field used as an impedance multiplier for segment length.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"Optional line field marking one-way digitized edges (supports FT/TF/B plus legacy boolean values).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Optional line field marking blocked/closed edges to exclude from routing (true/1/yes blocks).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"node_cost_points","description":"Optional point layer providing per-node entry costs (e.g., intersection delay).","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"node_cost_field","description":"Numeric field in node_cost_points containing non-negative per-node costs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"node_cost_snap_distance","description":"Optional max distance from each node-cost point to a network node for assignment.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"parallel_execution","description":"If true (default), evaluate origins in parallel for faster accessibility computation.","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"output","description":"Output point vector path (origins with accessibility metrics).","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"network_centrality_metrics","display_name":"Network Centrality Metrics","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input line network layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Optional node snapping tolerance for graph construction.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"edge_cost_field","description":"Optional numeric line field used as an impedance multiplier for segment length.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"Optional line field marking one-way digitized edges (supports FT/TF/B plus legacy boolean values).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Optional line field marking blocked/closed edges to exclude from graph construction (true/1/yes blocks).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output point vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"network_connected_components","display_name":"Network Connected Components","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"network_node_degree","display_name":"Network Node Degree","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"network_od_cost_matrix","display_name":"Network Od Cost Matrix","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"origins","description":"Origins","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"destinations","description":"Destinations","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Max Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"edge_cost_field","description":"Edge Cost Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"One Way Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Blocked Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"barriers","description":"Barriers","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"barrier_snap_distance","description":"Barrier Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"turn_penalty","description":"Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"u_turn_penalty","description":"U Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"forbid_u_turns","description":"Forbid U Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_left_turns","description":"Forbid Left Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_right_turns","description":"Forbid Right Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"turn_restrictions_csv","description":"Turn Restrictions Csv","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_cost_profile","description":"Temporal Cost Profile","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Temporal Edge Id Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Departure Time","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Temporal Mode","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_fallback","description":"Temporal Fallback","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_profile_report","description":"Temporal Profile Report","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"network_readiness_and_diagnostics_intelligence","display_name":"Network Readiness And Diagnostics Intelligence","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"network","description":"Input line network layer.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"qa_report","description":"Output CSV with QA findings.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"diagnostics_layer","description":"Output GeoJSON/GeoPackage with diagnostics.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"readiness_score","description":"Output JSON with readiness score.","type":"json","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"html_report","description":"Optional output path for an HTML network readiness diagnostic summary report.","type":"text","required":false,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"network_routes_from_od","display_name":"Network Routes From Od","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"origins","description":"Origins","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"destinations","description":"Destinations","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Max Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"edge_cost_field","description":"Edge Cost Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"One Way Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Blocked Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"barriers","description":"Barriers","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"barrier_snap_distance","description":"Barrier Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"turn_penalty","description":"Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"u_turn_penalty","description":"U Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"forbid_u_turns","description":"Forbid U Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_left_turns","description":"Forbid Left Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_right_turns","description":"Forbid Right Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"turn_restrictions_csv","description":"Turn Restrictions Csv","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_cost_profile","description":"Temporal Cost Profile","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Temporal Edge Id Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Departure Time","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Temporal Mode","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_fallback","description":"Temporal Fallback","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_profile_report","description":"Temporal Profile Report","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"network_service_area","display_name":"Network Service Area","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"origins","description":"Origins","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"max_cost","description":"Max Cost","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Max Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output_mode","description":"Output Mode","type":"str","required":false,"default":"nodes","options":[],"kind":"string"},{"name":"polygon_merge_origins","description":"Polygon Merge Origins","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"mode_field","description":"Mode Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"default_mode_speed","description":"Default Mode Speed","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"mode_speed_overrides","description":"Mode Speed Overrides","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"allowed_modes","description":"Allowed Modes","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"edge_cost_field","description":"Edge Cost Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"One Way Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Blocked Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"barriers","description":"Barriers","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"barrier_snap_distance","description":"Barrier Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"turn_penalty","description":"Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"u_turn_penalty","description":"U Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"forbid_u_turns","description":"Forbid U Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_left_turns","description":"Forbid Left Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_right_turns","description":"Forbid Right Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"turn_restrictions_csv","description":"Turn Restrictions Csv","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_cost_profile","description":"Temporal Cost Profile","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Temporal Edge Id Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Departure Time","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Temporal Mode","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_fallback","description":"Temporal Fallback","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_profile_report","description":"Temporal Profile Report","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"network_topology_audit","display_name":"Network Topology Audit","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"one_way_field","description":"One Way Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Blocked Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"report","description":"Report","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"od_sensitivity_analysis","display_name":"Od Sensitivity Analysis","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input line network layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"origins","description":"Origin point layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"destinations","description":"Destination point layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"edge_cost_field","description":"Required numeric line field used as an impedance multiplier for perturbation analysis.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"impedance_disturbance_range","description":"Range for cost perturbation as 'min_factor,max_factor' (e.g., '0.8,1.2' for ±20% variation).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"monte_carlo_samples","description":"Number of Monte Carlo samples for perturbation analysis (default 1, max 100).","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"snap_tolerance","description":"Optional node snapping tolerance for graph construction.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Optional max distance from origin/destination points to nearest network node.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"one_way_field","description":"Optional line field marking one-way digitized edges (supports FT/TF/B plus legacy boolean values).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Optional line field marking blocked/closed edges.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"node_cost_points","description":"Optional point layer providing per-node entry costs (e.g., intersection delay).","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"node_cost_field","description":"Numeric field in node_cost_points containing non-negative per-node costs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"node_cost_snap_distance","description":"Optional max distance from each node-cost point to a network node for assignment.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"parallel_execution","description":"If true (default), evaluates origin searches in parallel for baseline and perturbed OD runs.","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"output","description":"Output CSV path with OD pairs and sensitivity statistics.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"shortest_path_network","display_name":"Shortest Path Network","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"start_x","description":"Start X","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"start_y","description":"Start Y","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"end_x","description":"End X","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"end_y","description":"End Y","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_snap_distance","description":"Max Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"edge_cost_field","description":"Edge Cost Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"One Way Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"blocked_field","description":"Blocked Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"barriers","description":"Barriers","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"barrier_snap_distance","description":"Barrier Snap Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"turn_penalty","description":"Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"u_turn_penalty","description":"U Turn Penalty","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"forbid_u_turns","description":"Forbid U Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_left_turns","description":"Forbid Left Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"forbid_right_turns","description":"Forbid Right Turns","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"turn_restrictions_csv","description":"Turn Restrictions Csv","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_cost_profile","description":"Temporal Cost Profile","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Temporal Edge Id Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Departure Time","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Temporal Mode","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_fallback","description":"Temporal Fallback","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_profile_report","description":"Temporal Profile Report","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"split_lines_at_intersections","display_name":"Split Lines At Intersections","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input line layer to split at intersections.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Optional snapping tolerance used by split operations; defaults to machine epsilon.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output split line vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"snap_points_to_network","display_name":"Snap Points To Network","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Input point layer to be snapped.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"network","description":"Input network line layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"max_snap_distance","description":"Optional maximum snapping distance in map units.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"keep_unsnapped","description":"If true (default), retains points that cannot be snapped within tolerance.","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"output","description":"Output snapped point vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"service_area_planning_and_coverage_optimization","display_name":"Service Area Planning And Coverage Optimization","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"network","description":"Input line network layer.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"facilities","description":"Input facility points layer.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"demand_points","description":"Optional demand layer (points or polygons).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"ring_costs","description":"Numeric array of ring costs.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"scenarios","description":"Optional scenario CSV.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"service_areas","description":"Output service area vectors.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"service_area_merge_origins","description":"If true, dissolve overlapping origin polygons into merged coverage per ring for service_areas output (default false).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"uncovered_demand","description":"Output uncovered demand vectors.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"scenario_summary_csv","description":"Output scenario summary CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"ranked_candidates_csv","description":"Output ranked candidates CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"optimization_mode","description":"Optimization mode: existing or expansion.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"candidate_mode","description":"Expansion candidate mode: user_supplied or generated.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"candidate_sites","description":"Optional candidate points layer for user_supplied expansion mode.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"num_sites_to_select","description":"Number of top expansion sites to select.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"selection_strategy","description":"Expansion strategy: single_best or top_k_greedy.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"min_new_site_separation","description":"Minimum separation between selected expansion sites in map units.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_generated_candidates","description":"Maximum number of generated expansion candidates.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"ranked_candidates_vector","description":"Optional vector output path for ranked candidates with metrics.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_out"},{"name":"html_report","description":"Optional output path for an HTML service area planning summary report.","type":"text","required":false,"default":null,"options":[],"kind":"file_out"},{"name":"cost_method","description":"Cost method: distance (default), mode_speed, edge_field, or temporal.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"edge_cost_field","description":"Optional numeric line field used as an impedance multiplier for segment length.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"one_way_field","description":"Optional line field marking one-way digitized edges (supports FT/TF/B plus legacy boolean values).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"mode_field","description":"Optional line attribute field identifying travel mode per segment; enables mode-aware service-area costs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"default_mode_speed","description":"Default mode speed in coordinate-units per time unit when mode_field is provided (default: 1).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"mode_speed_overrides","description":"Optional comma-separated mode:speed overrides (e.g., walk:1.4,drive:12).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"allowed_modes","description":"Optional comma-separated allow-list of modes to include when mode_field is provided.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_cost_profile","description":"Optional CSV defining time-dependent edge costs (columns: edge_id,dow,start_minute,end_minute,value).","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"temporal_edge_id_field","description":"Optional network field used to match temporal_cost_profile edge_id values (default EDGE_ID).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"departure_time","description":"Optional RFC3339 departure time used for temporal profile lookup.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_mode","description":"Optional temporal interpretation mode: multiplier or absolute.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"temporal_fallback","description":"Optional fallback when temporal row is missing: static_cost or error.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"turn_penalty","description":"Optional additive cost applied to non-straight turns at network nodes.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"u_turn_penalty","description":"Optional additive cost applied to U-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"forbid_u_turns","description":"If true, disallow U-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"forbid_left_turns","description":"If true, disallow left-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"forbid_right_turns","description":"If true, disallow right-turn transitions.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"turn_restrictions_csv","description":"Optional CSV of turn transitions using columns prev_x,prev_y,node_x,node_y,next_x,next_y. Optional columns: forbidden (default true when no turn_cost column is provided) and turn_cost (or penalty/cost/extra_cost) for per-turn additive cost.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"node_cost_points","description":"Optional point layer providing per-node entry costs (e.g., intersection delay).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"node_cost_field","description":"Numeric field in node_cost_points containing non-negative per-node costs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"node_cost_snap_distance","description":"Optional max distance from each node-cost point to a network node for assignment.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"transfer_attributes","display_name":"Transfer Attributes","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"target","description":"Target layer receiving transferred attributes.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"source","description":"Source layer providing attributes.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"predicate","description":"Spatial predicate: intersects, within, contains, touches, crosses, overlaps, within_distance.","type":"string","required":false,"default":null,"options":["intersects","within","contains","touches","crosses","overlaps","within_distance"],"kind":"enum"},{"name":"distance","description":"Distance threshold when predicate=within_distance.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"strategy","description":"Transfer strategy: first, last, count, sum, mean, min, max.","type":"string","required":false,"default":null,"options":["first","last","count","sum","mean","min","max"],"kind":"enum"},{"name":"prefix","description":"Prefix for transferred source fields (default SRC_).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"travelling_salesman_problem","display_name":"Travelling Salesman Problem","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"duration","description":"Duration","type":"int","required":false,"default":60,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"vehicle_routing_cvrp","display_name":"Vehicle Routing Cvrp","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"network","description":"Input line network layer (validated for contract parity).","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"depot_points","description":"Depot point layer; each point can contribute one or more vehicles.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"stop_points","description":"Delivery stop point layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"demand_field","description":"Numeric demand field in stop_points (default: demand).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"priority_field","description":"Optional stop priority field using values like required/high/normal/low or numeric ranks.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"allowed_vehicle_profiles_field","description":"Optional stop field listing compatible vehicle profiles (comma/semicolon/pipe-delimited).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"allowed_route_classes_field","description":"Optional alias of allowed_vehicle_profiles_field for route-class compatibility rules.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"depot_id_field","description":"Optional depot ID field used in route/assignment outputs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_count_field","description":"Optional depot field for number of vehicles spawned at each depot.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_capacity_field","description":"Optional depot field overriding vehicle_capacity per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_fixed_cost_field","description":"Optional depot field overriding vehicle_fixed_cost per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"travel_speed_field","description":"Optional depot field overriding travel_speed per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_route_distance_field","description":"Optional depot field overriding max_route_distance per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_route_time_field","description":"Optional depot field overriding max_route_time per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_profile_field","description":"Optional depot field defining vehicle profile/category token used for stop compatibility.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_route_class_field","description":"Optional alias of vehicle_profile_field for route-class compatibility rules.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_capacity","description":"Per-vehicle capacity (> 0).","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"vehicle_fixed_cost","description":"Optional fixed cost charged per dispatched vehicle/route (default: 0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_vehicles","description":"Optional maximum number of vehicles/routes to construct.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"max_route_distance","description":"Optional maximum travel distance per route, including return to depot.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"travel_speed","description":"Travel speed in coordinate-units per time unit (default: 1).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_route_time","description":"Optional maximum route duration in model time units, including return to depot.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_stops_per_vehicle","description":"Optional maximum number of stops assigned to each vehicle route.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"objective_mode","description":"Route-construction objective: minimize_distance, minimize_vehicles, or minimize_cost.","type":"string","required":false,"default":null,"options":["minimize_distance","minimize_vehicles","minimize_cost"],"kind":"enum"},{"name":"apply_local_optimization","description":"When true, applies a deterministic 2-opt local improvement pass to each constructed route (default: true).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"apply_simulated_annealing","description":"When true, applies a seeded simulated annealing refinement pass per route after greedy/local optimization (default: false).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"sa_iterations","description":"Maximum simulated annealing iterations per route when apply_simulated_annealing=true (default: 1500).","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"sa_initial_temperature","description":"Initial simulated annealing temperature (> 0, default: 1.0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"sa_cooling_rate","description":"Simulated annealing cooling multiplier in (0, 1); default 0.995.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"sa_seed","description":"Optional deterministic random seed for simulated annealing (default: 42).","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output route line vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"assignment_output","description":"Optional stop assignment point output with visit order/load diagnostics.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"vehicle_routing_pickup_delivery","display_name":"Vehicle Routing Pickup Delivery","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"network","description":"Input line network layer (validated for contract parity).","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"depot_points","description":"Depot point layer; first point is used as the active depot in this baseline implementation.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"stop_points","description":"Stop point layer containing paired pickup and delivery records.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"request_id_field","description":"Request identifier field in stop_points used to pair pickup and delivery records (default: request_id).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"stop_type_field","description":"Stop type field in stop_points containing pickup/delivery labels (default: stop_type).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"demand_field","description":"Numeric demand field in stop_points; pickup demand is loaded and delivered demand is ignored (default: demand).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_capacity","description":"Per-vehicle capacity (> 0).","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"max_vehicles","description":"Optional maximum number of vehicles/routes to construct.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output route line vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"assignment_output","description":"Optional stop assignment point output with request and precedence diagnostics.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"vehicle_routing_vrptw","display_name":"Vehicle Routing Vrptw","summary":"","category":"Vector - Network Analysis","taxonomy_category":"vector","taxonomy_subcategory":"network_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"network","description":"Input line network layer (validated for contract parity).","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"depot_points","description":"Depot point layer; each point can contribute one or more vehicles.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"stop_points","description":"Delivery stop point layer with demand and time-window fields.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"demand_field","description":"Numeric demand field in stop_points (default: demand).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"priority_field","description":"Optional stop priority field using values like required/high/normal/low or numeric ranks.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"allowed_vehicle_profiles_field","description":"Optional stop field listing compatible vehicle profiles (comma/semicolon/pipe-delimited).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"allowed_route_classes_field","description":"Optional alias of allowed_vehicle_profiles_field for route-class compatibility rules.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"tw_start_field","description":"Numeric time-window start field in stop_points (default: tw_start).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"tw_end_field","description":"Numeric time-window end field in stop_points (default: tw_end).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"service_time_field","description":"Numeric per-stop service time field in stop_points (default: service_time).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"depot_id_field","description":"Optional depot ID field used in route/assignment outputs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_count_field","description":"Optional depot field for number of vehicles spawned at each depot.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_capacity_field","description":"Optional depot field overriding vehicle_capacity per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_fixed_cost_field","description":"Optional depot field overriding vehicle_fixed_cost per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"travel_speed_field","description":"Optional depot field overriding travel_speed per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_route_distance_field","description":"Optional depot field overriding max_route_distance per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_route_time_field","description":"Optional depot field overriding max_route_time per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_profile_field","description":"Optional depot field defining vehicle profile/category token used for stop compatibility.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_route_class_field","description":"Optional alias of vehicle_profile_field for route-class compatibility rules.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"depot_close_time_field","description":"Optional depot field overriding depot_close_time per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"break_start_field","description":"Optional depot field overriding break_start_time per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"break_end_field","description":"Optional depot field overriding break_end_time per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"break_duration_field","description":"Optional depot field overriding break_duration per depot/vehicle template.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"vehicle_capacity","description":"Per-vehicle capacity (> 0).","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"vehicle_fixed_cost","description":"Optional fixed cost charged per dispatched vehicle/route (default: 0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"start_time","description":"Route start time in model time units (default: 0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"travel_speed","description":"Travel speed in coordinate-units per time unit (default: 1).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"enforce_time_windows","description":"When true, only stops with lateness <= allowed_lateness are eligible for assignment (default: false).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"allowed_lateness","description":"Maximum lateness tolerated when enforce_time_windows=true (default: 0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"depot_close_time","description":"Optional hard close time by which each route must return to depot.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"break_start_time","description":"Optional global break-window start time for all vehicles.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"break_end_time","description":"Optional global break-window end time for all vehicles.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"break_duration","description":"Optional global break duration applied once per route when break window is intersected.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"use_priority_scoring","description":"When true, ranks feasible candidates by projected lateness/slack before travel distance; when false, uses nearest-neighbour baseline (default: true).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"max_vehicles","description":"Optional maximum number of vehicles/routes to construct.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"max_route_distance","description":"Optional maximum route travel distance, including return to depot.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_route_time","description":"Optional maximum route duration in model time units, including return to depot.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_stops_per_vehicle","description":"Optional maximum number of stops assigned to each vehicle route.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"objective_mode","description":"Route-construction objective: minimize_lateness, minimize_distance, minimize_vehicles, or minimize_cost.","type":"string","required":false,"default":null,"options":["minimize_lateness","minimize_distance","minimize_vehicles","minimize_cost"],"kind":"enum"},{"name":"output","description":"Output route line vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"assignment_output","description":"Optional stop assignment point output with time-window diagnostics.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"clip","display_name":"Clip","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"overlay","description":"Overlay","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"}],"return_type":"Vector"},{"id":"difference","display_name":"Difference","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"overlay","description":"Overlay","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"}],"return_type":"Vector"},{"id":"dissolve","display_name":"Dissolve","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"dissolve_field","description":"Dissolve Field","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float","required":false,"default":2.220446049250313e-16,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"erase","display_name":"Erase","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"overlay","description":"Overlay","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"}],"return_type":"Vector"},{"id":"identity","display_name":"Identity","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"overlay","description":"Overlay","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"}],"return_type":"Vector"},{"id":"intersect","display_name":"Intersect","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"overlay","description":"Overlay","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"}],"return_type":"Vector"},{"id":"line_intersections","display_name":"Line Intersections","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"input2","description":"Input2","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"}],"return_type":"Vector"},{"id":"line_polygon_clip","display_name":"Line Polygon Clip","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"clip","description":"Clip","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"near","display_name":"Near","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"near","description":"Near","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"max_distance","description":"Max Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"select_by_location","display_name":"Select By Location","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"target","description":"Target","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"query","description":"Query","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"predicate","description":"Predicate","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"distance","description":"Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"spatial_join","display_name":"Spatial Join","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"target","description":"Target","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"join","description":"Join","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"predicate","description":"Predicate","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"distance","description":"Distance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"strategy","description":"Strategy","type":"Literal[\"first\", \"last\", \"count\", \"sum\", \"mean\", \"min\", \"max\"]","required":false,"default":"first","options":[],"kind":"string"},{"name":"prefix","description":"Prefix","type":"str","required":false,"default":"JOIN_","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"symmetrical_difference","display_name":"Symmetrical Difference","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"overlay","description":"Overlay","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"}],"return_type":"Vector"},{"id":"union","display_name":"Union","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"overlay","description":"Overlay","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"}],"return_type":"Vector"},{"id":"update","display_name":"Update","summary":"","category":"Vector - Overlay Analysis","taxonomy_category":"vector","taxonomy_subcategory":"overlay_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"overlay","description":"Overlay","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float | None","required":false,"default":null,"options":[],"kind":"double"}],"return_type":"Vector"},{"id":"centroid_vector","display_name":"Centroid Vector","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"concave_hull","display_name":"Concave Hull","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"max_edge_length","description":"Max Edge Length","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"epsilon","description":"Epsilon","type":"float","required":false,"default":1e-9,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"densify_features","display_name":"Densify Features","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"spacing","description":"Spacing","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"eliminate_coincident_points","display_name":"Eliminate Coincident Points","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"tolerance_dist","description":"Tolerance Dist","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"extend_vector_lines","display_name":"Extend Vector Lines","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"distance","description":"Distance","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"extend_direction","description":"Extend Direction","type":"Literal[\"both\", \"start\", \"end\"]","required":false,"default":"both","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"merge_line_segments","display_name":"Merge Line Segments","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float","required":false,"default":2.220446049250313e-16,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"minimum_bounding_box","display_name":"Minimum Bounding Box","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"min_criteria","description":"Min Criteria","type":"Literal[\"area\", \"perimeter\", \"length\", \"width\"]","required":false,"default":"area","options":[],"kind":"string"},{"name":"individual_feature_hulls","description":"Individual Feature Hulls","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"minimum_bounding_circle","display_name":"Minimum Bounding Circle","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"individual_feature_hulls","description":"Individual Feature Hulls","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"minimum_bounding_envelope","display_name":"Minimum Bounding Envelope","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"individual_feature_hulls","description":"Individual Feature Hulls","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"minimum_convex_hull","display_name":"Minimum Convex Hull","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"individual_feature_hulls","description":"Individual Feature Hulls","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"polygonize","display_name":"Polygonize","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_layers","description":"Input Layers","type":"list[Vector | str]","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float","required":false,"default":2.220446049250313e-16,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"representative_point_vector","display_name":"Representative Point Vector","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output point vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"simplify_features","display_name":"Simplify Features","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"tolerance","description":"Tolerance","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"smooth_vectors","display_name":"Smooth Vectors","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":3,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"snap_endnodes","display_name":"Snap Endnodes","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float","required":false,"default":2.220446049250313e-16,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"split_vector_lines","display_name":"Split Vector Lines","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"segment_length","description":"Segment Length","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"split_with_lines","display_name":"Split With Lines","summary":"","category":"Vector - Geometry Processing","taxonomy_category":"vector","taxonomy_subcategory":"geometry_processing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"split_vector","description":"Split Vector","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_tolerance","description":"Snap Tolerance","type":"float","required":false,"default":2.220446049250313e-16,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"compactness_ratio","display_name":"Compactness Ratio","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"deviation_from_regional_direction","display_name":"Deviation From Regional Direction","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"elongation_threshold","description":"Elongation Threshold","type":"float","required":false,"default":0.75,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"elongation_ratio","display_name":"Elongation Ratio","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"hole_proportion","display_name":"Hole Proportion","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"linearity_index","display_name":"Linearity Index","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"narrowness_index_vector","display_name":"Narrowness Index Vector","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"patch_orientation","display_name":"Patch Orientation","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"perimeter_area_ratio","display_name":"Perimeter Area Ratio","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"polygon_area","display_name":"Polygon Area","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"polygon_long_axis","display_name":"Polygon Long Axis","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"polygon_perimeter","display_name":"Polygon Perimeter","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"polygon_short_axis","display_name":"Polygon Short Axis","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"related_circumscribing_circle","display_name":"Related Circumscribing Circle","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"shape_complexity_index_vector","display_name":"Shape Complexity Index Vector","summary":"","category":"Vector - Shape Metrics","taxonomy_category":"vector","taxonomy_subcategory":"shape_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"global_morans_i","display_name":"Global Morans I","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field","description":"Numeric attribute field to analyze.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"weights_mode","description":"Neighborhood mode: queen, rook, k_nearest, distance_band.","type":"string","required":false,"default":null,"options":["queen","rook","k_nearest","distance_band"],"kind":"enum"},{"name":"k","description":"k value for k_nearest mode.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"distance","description":"Distance threshold for distance_band mode.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"row_standardize","description":"Apply row standardization to weights (default true).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"inference","description":"Inference mode: asymptotic or permutation (default: asymptotic).","type":"string","required":false,"default":null,"options":["asymptotic","permutation"],"kind":"enum"},{"name":"num_simulations","description":"Number of permutations for permutation testing (default: 999).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"seed","description":"Random seed for reproducible permutation testing.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"island_policy","description":"Island handling: drop_with_warning, keep_zero_weight, error.","type":"string","required":false,"default":null,"options":["drop_with_warning","keep_zero_weight","error"],"kind":"enum"},{"name":"output_json","description":"Optional JSON report output path.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_html","description":"Optional HTML report output path.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_csv","description":"Optional CSV summary output path.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_distribution","description":"Optional path to save permutation distribution as JSON.","type":"json","required":false,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"getis_ord_gi_star","display_name":"Getis Ord Gi Star","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field","description":"Numeric attribute field to analyze.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"weights_mode","description":"Neighborhood mode: queen, rook, k_nearest, distance_band.","type":"string","required":false,"default":null,"options":["queen","rook","k_nearest","distance_band"],"kind":"enum"},{"name":"k","description":"k value for k_nearest mode.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"distance","description":"Distance threshold for distance_band mode.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"row_standardize","description":"Apply row standardization to weights (default true).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"variant","description":"Variant: gi or gi_star (default gi_star).","type":"string","required":false,"default":null,"options":["gi","gi_star"],"kind":"enum"},{"name":"inference","description":"Inference mode: asymptotic or permutation (default: asymptotic).","type":"string","required":false,"default":null,"options":["asymptotic","permutation"],"kind":"enum"},{"name":"num_simulations","description":"Number of permutations for permutation testing (default: 999).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"seed","description":"Random seed for reproducible permutation testing.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"island_policy","description":"Island handling: drop_with_warning, keep_zero_weight, error.","type":"string","required":false,"default":null,"options":["drop_with_warning","keep_zero_weight","error"],"kind":"enum"},{"name":"alpha","description":"Significance threshold in [0, 1]; default 0.05.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output vector path with GI fields.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"output_html","description":"Optional HTML report output path.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"local_morans_i_lisa","display_name":"Local Morans I Lisa","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field","description":"Numeric attribute field to analyze.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"weights_mode","description":"Neighborhood mode: queen, rook, k_nearest, distance_band.","type":"string","required":false,"default":null,"options":["queen","rook","k_nearest","distance_band"],"kind":"enum"},{"name":"k","description":"k value for k_nearest mode.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"distance","description":"Distance threshold for distance_band mode.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"row_standardize","description":"Apply row standardization to weights (default true).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"inference","description":"Inference mode: asymptotic or permutation (default: asymptotic).","type":"string","required":false,"default":null,"options":["asymptotic","permutation"],"kind":"enum"},{"name":"num_simulations","description":"Number of permutations for permutation testing (default: 999).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"seed","description":"Random seed for reproducible permutation testing.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"island_policy","description":"Island handling: drop_with_warning, keep_zero_weight, error.","type":"string","required":false,"default":null,"options":["drop_with_warning","keep_zero_weight","error"],"kind":"enum"},{"name":"alpha","description":"Significance threshold in [0, 1]; default 0.05.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"fdr_correction","description":"Apply FDR-BH correction (default true for permutation).","type":"lidar","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"output","description":"Output vector path with LISA fields.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"output_html","description":"Optional HTML report output path.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"nearest_neighbour_index","display_name":"Nearest Neighbour Index","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input point vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"study_area_mode","description":"Study area mode: hull, envelope, polygon_layer.","type":"string","required":false,"default":null,"options":["hull","envelope","polygon_layer"],"kind":"enum"},{"name":"study_area_polygon","description":"Polygon layer used when study_area_mode=polygon_layer.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_json","description":"Optional JSON report output path.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_html","description":"Optional HTML report output path.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_csv","description":"Optional CSV summary output path.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"quadrat_count_test","display_name":"Quadrat Count Test","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input point vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"grid_mode","description":"Grid mode: rows_cols or cell_size.","type":"string","required":false,"default":null,"options":["rows_cols","cell_size"],"kind":"enum"},{"name":"rows","description":"Rows when grid_mode=rows_cols.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"cols","description":"Cols when grid_mode=rows_cols.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"cell_size","description":"Cell size when grid_mode=cell_size.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"study_area_mode","description":"Study area mode: hull, envelope, polygon_layer.","type":"string","required":false,"default":null,"options":["hull","envelope","polygon_layer"],"kind":"enum"},{"name":"study_area_polygon","description":"Polygon layer used when study_area_mode=polygon_layer.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_grid","description":"Optional quadrat polygon grid output path.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_out"},{"name":"output_json","description":"Optional JSON report output path.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_html","description":"Optional HTML report output path.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_csv","description":"Optional CSV summary output path.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"spatial_lag_regression","display_name":"Spatial Lag Regression","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"response_field","description":"Response (dependent) variable.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"predictor_fields","description":"Comma-separated predictor field names.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"weights_mode","description":"Spatial neighborhood mode.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"k","description":"k value for k_nearest.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"distance","description":"Distance threshold.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"row_standardize","description":"Row standardize weights.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"spatial_error_regression","display_name":"Spatial Error Regression","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"response_field","description":"Response variable.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"predictor_fields","description":"Comma-separated predictor fields.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"weights_mode","description":"Spatial neighborhood mode.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"k","description":"k for k_nearest.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"distance","description":"Distance threshold.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"row_standardize","description":"Row standardize weights.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"geographically_weighted_regression","display_name":"Geographically Weighted Regression","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"response_field","description":"Response variable.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"predictor_fields","description":"Comma-separated predictor fields.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"bandwidth_hint","description":"Optional bandwidth hint.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"ripleys_k_test","display_name":"Ripleys K Test","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input point vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"max_distance","description":"Maximum analysis distance.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"step_size","description":"Distance step size.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output vector with K statistics.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"envelope_test","display_name":"Envelope Test","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input point vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"num_simulations","description":"Number of Monte Carlo simulations.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_distance","description":"Maximum analysis distance.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output vector with envelope bounds.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"point_process_residuals","display_name":"Point Process Residuals","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input point vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"intensity_field","description":"Fitted intensity field.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output vector with residuals.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"ripleys_k_function","display_name":"Ripleys K Function","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Point vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"max_distance","description":"Maximum distance threshold.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"num_distances","description":"Number of distance steps.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"point_pattern_envelope","display_name":"Point Pattern Envelope","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Point vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"max_distance","description":"Maximum distance.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"num_distances","description":"Distance bins.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"num_simulations","description":"Monte Carlo replicates.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"alpha","description":"Significance level.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"inhomogeneous_baseline","display_name":"Inhomogeneous Baseline","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Point vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"max_distance","description":"Maximum distance.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"num_distances","description":"Distance bins.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"bandwidth","description":"KDE bandwidth (auto-select if omitted).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"hotspot_vs_process","display_name":"Hotspot Vs Process","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Point vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"hotspot_field","description":"Hotspot classification field.","type":"lidar","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"intensity_field","description":"Intensity field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"point_process_residuals_comparison","display_name":"Point Process Residuals Comparison","summary":"","category":"Vector - Spatial Statistics","taxonomy_category":"vector","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Point vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"observed_field","description":"Observed values field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"predicted_field","description":"Predicted values field.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"residual_type","description":"Residual type.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output CSV.","type":"table","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"construct_vector_tin","display_name":"Construct Vector Tin","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_points","description":"Input Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":false,"default":"FID","options":[],"kind":"string"},{"name":"use_z","description":"Use Z","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"max_triangle_edge_length","description":"Max Triangle Edge Length","type":"float","required":false,"default":-1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"contours_from_points","display_name":"Contours From Points","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"use_z_values","description":"Use Z Values","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"max_triangle_edge_length","description":"Max Triangle Edge Length","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"contour_interval","description":"Contour Interval","type":"float","required":false,"default":10,"options":[],"kind":"double"},{"name":"base_contour","description":"Base Contour","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"smoothing_filter_size","description":"Smoothing Filter Size","type":"int","required":false,"default":9,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"contours_from_raster","display_name":"Contours From Raster","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"contour_interval","description":"Contour Interval","type":"float","required":false,"default":10,"options":[],"kind":"double"},{"name":"base_contour","description":"Base Contour","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"smoothing_filter_size","description":"Smoothing Filter Size","type":"int","required":false,"default":9,"options":[],"kind":"int"},{"name":"deflection_tolerance","description":"Deflection Tolerance","type":"float","required":false,"default":10,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"extract_nodes","display_name":"Extract Nodes","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"extract_raster_values_at_points","display_name":"Extract Raster Values At Points","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"rasters","description":"Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"tuple[Vector, str]"},{"id":"find_lowest_or_highest_points","display_name":"Find Lowest Or Highest Points","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output_type","description":"Output Type","type":"str","required":false,"default":"lowest","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"hexagonal_grid_from_raster_base","display_name":"Hexagonal Grid From Raster Base","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base","description":"Base","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"width","description":"Width","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"orientation","description":"Orientation","type":"str","required":false,"default":"h","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"hexagonal_grid_from_vector_base","display_name":"Hexagonal Grid From Vector Base","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base","description":"Base","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"width","description":"Width","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"orientation","description":"Orientation","type":"str","required":false,"default":"h","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"layer_footprint_raster","display_name":"Layer Footprint Raster","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"layer_footprint_vector","display_name":"Layer Footprint Vector","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"medoid","display_name":"Medoid","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"random_points_in_polygon","display_name":"Random Points In Polygon","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"num_points","description":"Num Points","type":"int","required":true,"default":null,"options":[],"kind":"int"},{"name":"seed","description":"Seed","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"rectangular_grid_from_raster_base","display_name":"Rectangular Grid From Raster Base","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base","description":"Base","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"width","description":"Width","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"height","description":"Height","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"x_origin","description":"X Origin","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"y_origin","description":"Y Origin","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"rectangular_grid_from_vector_base","display_name":"Rectangular Grid From Vector Base","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base","description":"Base","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"width","description":"Width","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"height","description":"Height","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"x_origin","description":"X Origin","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"y_origin","description":"Y Origin","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"vector_hex_binning","display_name":"Vector Hex Binning","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"vector_points","description":"Vector Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"width","description":"Width","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"orientation","description":"Orientation","type":"str","required":false,"default":"h","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"voronoi_diagram","display_name":"Voronoi Diagram","summary":"","category":"Vector - Sampling & Gridding","taxonomy_category":"vector","taxonomy_subcategory":"sampling_gridding","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_points","description":"Input Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"add_field","display_name":"Add Field","summary":"","category":"Vector - Attribute Analysis","taxonomy_category":"vector","taxonomy_subcategory":"attribute_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field","description":"Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"field_type","description":"Field Type","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"default","description":"Default","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"add_geometry_attributes","display_name":"Add Geometry Attributes","summary":"","category":"Vector - Attribute Analysis","taxonomy_category":"vector","taxonomy_subcategory":"attribute_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"area","description":"Area","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"length","description":"Length","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"perimeter","description":"Perimeter","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"centroid","description":"Centroid","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"attribute_correlation","display_name":"Attribute Correlation","summary":"","category":"Vector - Attribute Analysis","taxonomy_category":"vector","taxonomy_subcategory":"attribute_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"attribute_histogram","display_name":"Attribute Histogram","summary":"","category":"Vector - Attribute Analysis","taxonomy_category":"vector","taxonomy_subcategory":"attribute_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"attribute_scattergram","display_name":"Attribute Scattergram","summary":"","category":"Vector - Attribute Analysis","taxonomy_category":"vector","taxonomy_subcategory":"attribute_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name_x","description":"Field Name X","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"field_name_y","description":"Field Name Y","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"trendline","description":"Trendline","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"delete_field","display_name":"Delete Field","summary":"","category":"Vector - Attribute Analysis","taxonomy_category":"vector","taxonomy_subcategory":"attribute_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"fields","description":"Fields","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"extract_by_attribute","display_name":"Extract By Attribute","summary":"","category":"Vector - Attribute Analysis","taxonomy_category":"vector","taxonomy_subcategory":"attribute_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"statement","description":"Statement","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"field_calculator","display_name":"Field Calculator","summary":"","category":"Vector - Attribute Analysis","taxonomy_category":"vector","taxonomy_subcategory":"attribute_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field","description":"Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"expression","description":"Expression","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"field_type","description":"Field Type","type":"Literal[\"float\", \"integer\", \"text\"]","required":false,"default":"float","options":[],"kind":"int"},{"name":"overwrite","description":"Overwrite","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"filter_vector_features_by_area","display_name":"Filter Vector Features By Area","summary":"","category":"Vector - Attribute Analysis","taxonomy_category":"vector","taxonomy_subcategory":"attribute_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"threshold","description":"Threshold","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"list_unique_values","display_name":"List Unique Values","summary":"","category":"Vector - Attribute Analysis","taxonomy_category":"vector","taxonomy_subcategory":"attribute_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"rename_field","display_name":"Rename Field","summary":"","category":"Vector - Attribute Analysis","taxonomy_category":"vector","taxonomy_subcategory":"attribute_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field","description":"Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"new_field","description":"New Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"utility_corridor_encroachment_and_access_planning","display_name":"Utility Corridor Encroachment And Access Planning","summary":"","category":"Vector - Workflow Products","taxonomy_category":"vector","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"corridors","description":"Input corridor centerline layer (line geometry).","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"encroachments","description":"Input encroachment observations (point/line/polygon).","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"access_points","description":"Input field-access points (point geometry).","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"corridor_influence_distance","description":"Maximum distance from corridor to keep as a hotspot (default 30.0 map units).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"high_risk_distance","description":"Distance threshold considered highest risk (default 10.0 map units).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"hotspots","description":"Output hotspot vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"priority_csv","description":"Output ranked hotspot CSV path.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"planning_report","description":"Output planning summary JSON path.","type":"json","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"response_queue_csv","description":"Optional output CSV path for dispatch-ready response queue with SLA guidance.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"html_report","description":"Optional output path for an HTML operations dashboard report.","type":"text","required":false,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"parcel_and_land_fabric_topology_compliance_workflow","display_name":"Parcel And Land Fabric Topology Compliance Workflow","summary":"","category":"Vector - Workflow Products","taxonomy_category":"vector","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"parcels","description":"Input parcel polygon layer.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"min_sliver_area","description":"Minimum area threshold under which parcels are flagged as slivers (default 1.0).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"auto_fix","description":"If true, runs topology_rule_autofix and writes corrected output (default false).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"jurisdiction_template","description":"Optional rule-template preset (generic | ontario_mpac). Influences default sliver threshold and report recommendations.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"topology_violations","description":"Output vector path for topology rule violations.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"issues_csv","description":"Output CSV path for topology validation report.","type":"table","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"compliance_report","description":"Output JSON path for compliance summary report.","type":"json","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"corrected_parcels","description":"Optional output vector path for auto-fix corrected parcels.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"remediation_queue_csv","description":"Optional output CSV path for prioritized remediation actions by issue type.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"html_report","description":"Optional output HTML path for the compliance dashboard report.","type":"text","required":false,"default":null,"options":[],"kind":"file_in"}],"return_type":"Any"},{"id":"download_osm_vector","display_name":"Download Osm Vector","summary":"","category":"Vector - Online Data","taxonomy_category":"vector","taxonomy_subcategory":"online_data","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"west","description":"West boundary longitude (EPSG:4326).","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"south","description":"South boundary latitude (EPSG:4326).","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"east","description":"East boundary longitude (EPSG:4326).","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"north","description":"North boundary latitude (EPSG:4326).","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"input_extent_epsg","description":"EPSG code for west/south/east/north input extent (default 4326).","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"filter_preset","description":"Feature class preset: all, roads, buildings, water, landuse, trails, parks, rail, amenities, boundaries, transit, poi (default all).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"include_tags","description":"Semicolon-delimited tag keys filter list, e.g. amenity;shop.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"include_key_values","description":"Semicolon-delimited key=value filter list, e.g. amenity=school;building=yes.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"filter_key","description":"Legacy single tag-key filter (deprecated; use include_tags).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"filter_key_value","description":"Legacy single key=value filter (deprecated; use include_key_values).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"include_points","description":"Include OSM node features with tags as Point geometries (default true).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"include_lines","description":"Include OSM way features as LineString geometries (default true).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"include_polygons","description":"Include closed OSM ways with area tags as Polygon geometries (default true).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"clip_to_extent","description":"Clip output geometries to the query bbox extent (default true).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"split_output_by_geometry","description":"Write separate outputs per geometry type (_points|lines|polygons.ext).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"output_epsg","description":"Reproject output to this EPSG code; omit to keep EPSG:4326.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"overpass_profile","description":"Overpass endpoint profile: main, kumi, fr, or custom (default main).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"overpass_url","description":"Overpass API endpoint URL (default https://overpass-api.de/api/interpreter).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"timeout_seconds","description":"Query timeout in seconds (default 25).","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"max_elements","description":"Maximum number of returned Overpass elements before an error is raised (default 50 000).","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"chunk_large_aoi","description":"Automatically split larger AOIs into multiple Overpass requests (default true).","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"chunk_max_area_deg2","description":"Maximum area per chunk in square degrees when chunking (default 4.0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_chunk_count","description":"Hard cap on chunk count when chunking large AOIs (default 64).","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"chunk_parallel_requests","description":"Maximum parallel chunk requests for large AOIs (default 1 = sequential).","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"cache_dir","description":"Optional directory for caching Overpass JSON responses by endpoint+query hash.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cache_ttl_hours","description":"Cache time-to-live in hours (default 24; 0 means no expiry check).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"provenance_output","description":"Optional JSON path to write query/provenance metadata sidecar.","type":"json","required":false,"default":null,"options":[],"kind":"file_out"},{"name":"output","description":"Optional output vector path; when omitted, a temporary output is used.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Any"},{"id":"assign_projection_lidar","display_name":"Assign Projection Lidar","summary":"","category":"Projection and Georeferencing","taxonomy_category":"projection_georeferencing","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[],"return_type":"Any"},{"id":"assign_projection_raster","display_name":"Assign Projection Raster","summary":"","category":"Projection and Georeferencing","taxonomy_category":"projection_georeferencing","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[],"return_type":"Any"},{"id":"assign_projection_vector","display_name":"Assign Projection Vector","summary":"","category":"Projection and Georeferencing","taxonomy_category":"projection_georeferencing","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[],"return_type":"Any"},{"id":"georeference_raster_from_control_points","display_name":"Georeference Raster From Control Points","summary":"","category":"Projection and Georeferencing","taxonomy_category":"projection_georeferencing","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Any","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"control_points","description":"Control Points","type":"Any","required":true,"default":null,"options":[],"kind":"string"},{"name":"epsg","description":"Epsg","type":"Any","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"Any","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"report","description":"Report","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"resample","description":"Resample","type":"Any","required":false,"default":"bilinear","options":["nearest","bilinear","cubic","lanczos","average","min","max","mode","median"],"kind":"enum"}],"return_type":"Any"},{"id":"reproject_lidar","display_name":"Reproject Lidar","summary":"","category":"Projection and Georeferencing","taxonomy_category":"projection_georeferencing","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Any","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"dst_epsg","description":"Dst Epsg","type":"Any","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"Any","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"use_3d_transform","description":"Use 3d Transform","type":"Any","required":false,"default":false,"options":[],"kind":"bool"},{"name":"failure_policy","description":"Failure Policy","type":"Any","required":false,"default":"error","options":[],"kind":"string"},{"name":"coordinate_epoch","description":"Coordinate Epoch","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"source_reference_epoch","description":"Source Reference Epoch","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"target_reference_epoch","description":"Target Reference Epoch","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"operation_code","description":"Operation Code","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"prefer_official_operation","description":"Prefer Official Operation","type":"Any","required":false,"default":true,"options":[],"kind":"bool"},{"name":"epoch_policy","description":"Epoch Policy","type":"Any","required":false,"default":"strict","options":[],"kind":"string"}],"return_type":"Any"},{"id":"reproject_raster","display_name":"Reproject Raster","summary":"","category":"Projection and Georeferencing","taxonomy_category":"projection_georeferencing","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Any","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"dst_epsg","description":"Dst Epsg","type":"Any","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"Any","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"resample","description":"Resample","type":"Any","required":false,"default":"nearest","options":["nearest","bilinear","cubic","lanczos","average","min","max","mode","median"],"kind":"enum"},{"name":"cols","description":"Cols","type":"Any","required":false,"default":null,"options":[],"kind":"int"},{"name":"rows","description":"Rows","type":"Any","required":false,"default":null,"options":[],"kind":"int"},{"name":"extent","description":"Extent","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"x_res","description":"X Res","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"y_res","description":"Y Res","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"snap_x","description":"Snap X","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"snap_y","description":"Snap Y","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"nodata_policy","description":"Nodata Policy","type":"Any","required":false,"default":"partial_kernel","options":[],"kind":"string"},{"name":"antimeridian_policy","description":"Antimeridian Policy","type":"Any","required":false,"default":"auto","options":[],"kind":"string"},{"name":"grid_size_policy","description":"Grid Size Policy","type":"Any","required":false,"default":"expand","options":[],"kind":"string"},{"name":"destination_footprint","description":"Destination Footprint","type":"Any","required":false,"default":"none","options":[],"kind":"string"},{"name":"coordinate_epoch","description":"Coordinate Epoch","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"source_reference_epoch","description":"Source Reference Epoch","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"target_reference_epoch","description":"Target Reference Epoch","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"operation_code","description":"Operation Code","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"prefer_official_operation","description":"Prefer Official Operation","type":"Any","required":false,"default":true,"options":[],"kind":"bool"},{"name":"epoch_policy","description":"Epoch Policy","type":"Any","required":false,"default":"strict","options":[],"kind":"string"}],"return_type":"Any"},{"id":"reproject_vector","display_name":"Reproject Vector","summary":"","category":"Projection and Georeferencing","taxonomy_category":"projection_georeferencing","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Any","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"dst_epsg","description":"Dst Epsg","type":"Any","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"Any","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"failure_policy","description":"Failure Policy","type":"Any","required":false,"default":"error","options":[],"kind":"string"},{"name":"antimeridian_policy","description":"Antimeridian Policy","type":"Any","required":false,"default":"keep","options":[],"kind":"string"},{"name":"max_segment_length","description":"Max Segment Length","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"topology_policy","description":"Topology Policy","type":"Any","required":false,"default":"none","options":[],"kind":"string"},{"name":"coordinate_epoch","description":"Coordinate Epoch","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"source_reference_epoch","description":"Source Reference Epoch","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"target_reference_epoch","description":"Target Reference Epoch","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"operation_code","description":"Operation Code","type":"Any","required":false,"default":null,"options":[],"kind":"string"},{"name":"prefer_official_operation","description":"Prefer Official Operation","type":"Any","required":false,"default":true,"options":[],"kind":"bool"},{"name":"epoch_policy","description":"Epoch Policy","type":"Any","required":false,"default":"strict","options":[],"kind":"string"}],"return_type":"Any"},{"id":"orthorectification","display_name":"Orthorectification","summary":"","category":"Projection and Georeferencing","taxonomy_category":"projection_georeferencing","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_raster","description":"Input raw (uncorrected) image raster path. RPC metadata must be embedded in the file or present in an adjacent .RPB sidecar.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input_dem","description":"DEM raster used to obtain terrain elevation for each output pixel. Should cover the full scene extent. If not in geographic coordinates, the tool reprojects as needed.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output_epsg","description":"EPSG code of the output coordinate reference system (default 4326 geographic; specify a projected CRS like 32617 for UTM zone 17N).","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"output_resolution","description":"Output pixel resolution in the output CRS units (e.g., degrees for geographic, metres for projected). Defaults to the approximate GSD of the input image.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"resample_method","description":"Resampling method for output: nearest, bilinear (default), cubic.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"nodata_value","description":"No-data value to assign to pixels with no valid source coverage (default same as input).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output orthoimage raster path.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"average_flowpath_slope","display_name":"Average Flowpath Slope","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"average_upslope_flowpath_length","display_name":"Average Upslope Flowpath Length","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"d8_flow_accum","display_name":"D8 Flow Accum","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"out_type","description":"Out Type","type":"str","required":false,"default":"sca","options":[],"kind":"string"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"clip","description":"Clip","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"input_is_pointer","description":"Input Is Pointer","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"d8_mass_flux","display_name":"D8 Mass Flux","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"loading","description":"Loading","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"efficiency","description":"Efficiency","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"absorption","description":"Absorption","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"d8_pointer","display_name":"D8 Pointer","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"dinf_flow_accum","display_name":"Dinf Flow Accum","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"out_type","description":"Out Type","type":"str","required":false,"default":"sca","options":[],"kind":"string"},{"name":"convergence_threshold","description":"Convergence Threshold","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"clip","description":"Clip","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"input_is_pointer","description":"Input Is Pointer","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"dinf_mass_flux","display_name":"Dinf Mass Flux","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"loading","description":"Loading","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"efficiency","description":"Efficiency","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"absorption","description":"Absorption","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"dinf_pointer","display_name":"Dinf Pointer","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"downslope_flowpath_length","display_name":"Downslope Flowpath Length","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"watersheds","description":"Watersheds","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"weights","description":"Weights","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"fd8_flow_accum","display_name":"Fd8 Flow Accum","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"out_type","description":"Out Type","type":"str","required":false,"default":"sca","options":[],"kind":"string"},{"name":"exponent","description":"Exponent","type":"float","required":false,"default":1.1,"options":[],"kind":"double"},{"name":"convergence_threshold","description":"Convergence Threshold","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"clip","description":"Clip","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"fd8_pointer","display_name":"Fd8 Pointer","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"flow_accum_full_workflow","display_name":"Flow Accum Full Workflow","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"out_type","description":"Out Type","type":"str","required":false,"default":"sca","options":[],"kind":"string"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"clip","description":"Clip","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"breached_dem_output_path","description":"Breached Dem Output Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"flow_dir_output_path","description":"Flow Dir Output Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster, Raster]"},{"id":"flow_length_diff","display_name":"Flow Length Diff","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"max_upslope_flowpath_length","display_name":"Max Upslope Flowpath Length","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"max_upslope_value","display_name":"Max Upslope Value","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"values","description":"Values","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"mdinf_flow_accum","display_name":"Mdinf Flow Accum","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"out_type","description":"Out Type","type":"str","required":false,"default":"sca","options":[],"kind":"string"},{"name":"exponent","description":"Exponent","type":"float","required":false,"default":1.1,"options":[],"kind":"double"},{"name":"threshold","description":"Threshold","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"log","description":"Log","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"clip","description":"Clip","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"minimal_dispersion_flow_algorithm","display_name":"Minimal Dispersion Flow Algorithm","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"out_type","description":"Out Type","type":"str","required":false,"default":"sca","options":[],"kind":"string"},{"name":"path_corrected_direction_preference","description":"Path Corrected Direction Preference","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"clip","description":"Clip","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"flow_dir_output_path","description":"Flow Dir Output Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"num_inflowing_neighbours","display_name":"Num Inflowing Neighbours","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"qin_flow_accumulation","display_name":"Qin Flow Accumulation","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"out_type","description":"Out Type","type":"str","required":false,"default":"sca","options":[],"kind":"string"},{"name":"exponent","description":"Exponent","type":"float","required":false,"default":10,"options":[],"kind":"double"},{"name":"max_slope","description":"Max Slope","type":"float","required":false,"default":45,"options":[],"kind":"double"},{"name":"threshold","description":"Threshold","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"log","description":"Log","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"clip","description":"Clip","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"quinn_flow_accumulation","display_name":"Quinn Flow Accumulation","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"out_type","description":"Out Type","type":"str","required":false,"default":"sca","options":[],"kind":"string"},{"name":"exponent","description":"Exponent","type":"float","required":false,"default":1.1,"options":[],"kind":"double"},{"name":"threshold","description":"Threshold","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"log","description":"Log","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"clip","description":"Clip","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"rho8_flow_accum","display_name":"Rho8 Flow Accum","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"out_type","description":"Out Type","type":"str","required":false,"default":"sca","options":[],"kind":"string"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"clip","description":"Clip","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"input_is_pointer","description":"Input Is Pointer","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"rho8_pointer","display_name":"Rho8 Pointer","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"trace_downslope_flowpaths","display_name":"Trace Downslope Flowpaths","summary":"","category":"Hydrology - Flow Routing","taxonomy_category":"hydrology","taxonomy_subcategory":"flow_routing","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"seed_points","description":"Seed Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"d8_pointer","description":"D8 Pointer","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"breach_depressions_least_cost","display_name":"Breach Depressions Least Cost","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"max_cost","description":"Max Cost","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_dist","description":"Max Dist","type":"int","required":false,"default":100,"options":[],"kind":"int"},{"name":"flat_increment","description":"Flat Increment","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"fill_deps","description":"Fill Deps","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"minimize_dist","description":"Minimize Dist","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"breach_single_cell_pits","display_name":"Breach Single Cell Pits","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"burn_streams","display_name":"Burn Streams","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"decrement_value","description":"Decrement Value","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"gradient_distance","description":"Gradient Distance","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"burn_streams_at_roads","display_name":"Burn Streams At Roads","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"roads","description":"Roads","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"width","description":"Width","type":"float | None","required":false,"default":null,"options":[],"kind":"double"}],"return_type":"Raster"},{"id":"depth_in_sink","display_name":"Depth In Sink","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"fill_burn","display_name":"Fill Burn","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"fill_depressions","display_name":"Fill Depressions","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"fix_flats","description":"Fix Flats","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"flat_increment","description":"Flat Increment","type":"float | None","required":false,"default":0.0001,"options":[],"kind":"double"},{"name":"max_depth","description":"Max Depth","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"flat_resolution","description":"Flat Resolution","type":"Literal[\"garbrecht_martz\", \"natural\"] | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"fill_depressions_planchon_and_darboux","display_name":"Fill Depressions Planchon And Darboux","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"fix_flats","description":"Fix Flats","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"flat_increment","description":"Flat Increment","type":"float | None","required":false,"default":0.0001,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"fill_depressions_wang_and_liu","display_name":"Fill Depressions Wang And Liu","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"fix_flats","description":"Fix Flats","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"flat_increment","description":"Flat Increment","type":"float | None","required":false,"default":0.0001,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"fill_pits","display_name":"Fill Pits","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"flatten_lakes","display_name":"Flatten Lakes","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"lakes","description":"Lakes","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"impoundment_size_index","display_name":"Impoundment Size Index","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"max_dam_length","description":"Max Dam Length","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output_mean","description":"Output Mean","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output_max","description":"Output Max","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output_volume","description":"Output Volume","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output_area","description":"Output Area","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output_height","description":"Output Height","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"mean_output_path","description":"Mean Output Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"max_output_path","description":"Max Output Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"volume_output_path","description":"Volume Output Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"area_output_path","description":"Area Output Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"dam_height_output_path","description":"Dam Height Output Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster | None, Raster | None, Raster | None, Raster | None, Raster | None]"},{"id":"insert_dams","display_name":"Insert Dams","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"dam_points","description":"Dam Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"dam_length","description":"Dam Length","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"raise_walls","display_name":"Raise Walls","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"walls","description":"Walls","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"breach_lines","description":"Breach Lines","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"wall_height","description":"Wall Height","type":"float","required":false,"default":100,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"sink","display_name":"Sink","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"stochastic_depression_analysis","display_name":"Stochastic Depression Analysis","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"rmse","description":"Rmse","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"range","description":"Range","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"iterations","description":"Iterations","type":"int","required":false,"default":100,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"topological_breach_burn","display_name":"Topological Breach Burn","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_distance","description":"Snap Distance","type":"float","required":false,"default":0.001,"options":[],"kind":"double"},{"name":"output_streams_path","description":"Output Streams Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_dem_path","description":"Output Dem Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_dir_path","description":"Output Dir Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_flow_accum_path","description":"Output Flow Accum Path","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster, Raster, Raster]"},{"id":"upslope_depression_storage","display_name":"Upslope Depression Storage","summary":"","category":"Hydrology - Depressions & Storage","taxonomy_category":"hydrology","taxonomy_subcategory":"depressions_storage","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"basins","display_name":"Basins","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"flood_order","display_name":"Flood Order","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"hillslopes","display_name":"Hillslopes","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"isobasins","display_name":"Isobasins","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"target_size","description":"Target Size","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"jenson_snap_pour_points","display_name":"Jenson Snap Pour Points","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"pour_pts","description":"Pour Pts","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"snap_dist","description":"Snap Dist","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"longest_flowpath","display_name":"Longest Flowpath","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"basins","description":"Basins","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"max_branch_length","display_name":"Max Branch Length","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"log","description":"Log","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"snap_pour_points","display_name":"Snap Pour Points","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"pour_pts","description":"Pour Pts","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"flow_accum","description":"Flow Accum","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"snap_dist","description":"Snap Dist","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"subbasins","display_name":"Subbasins","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"unnest_basins","display_name":"Unnest Basins","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pointer","description":"D8 Pointer","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"pour_points","description":"Pour Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"list[Raster]"},{"id":"watershed","display_name":"Watershed","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"pour_pts","description":"Pour Pts","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"watershed_from_raster_pour_points","display_name":"Watershed From Raster Pour Points","summary":"","category":"Hydrology - Watersheds & Basins","taxonomy_category":"hydrology","taxonomy_subcategory":"watersheds_basins","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pointer","description":"D8 Pointer","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"pour_points","description":"Pour Points","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"depth_to_water","display_name":"Depth To Water","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"lakes","description":"Lakes","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"distance_to_outlet","display_name":"Distance To Outlet","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pointer","description":"D8 Pointer","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams_raster","description":"Streams Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"esri_pointer","description":"Esri Pointer","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"downslope_distance_to_stream","display_name":"Downslope Distance To Stream","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"dinf","description":"Dinf","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"downslope_index","display_name":"Downslope Index","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"vertical_drop","description":"Vertical Drop","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"output_type","description":"Output Type","type":"str","required":false,"default":"tangent","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"edge_contamination","display_name":"Edge Contamination","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"flow_type","description":"Flow Type","type":"str","required":false,"default":"mfd","options":[],"kind":"string"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":-1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"elev_relative_to_watershed_min_max","display_name":"Elev Relative To Watershed Min Max","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"watersheds","description":"Watersheds","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"elevation_above_stream","display_name":"Elevation Above Stream","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"elevation_above_stream_euclidean","display_name":"Elevation Above Stream Euclidean","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"find_noflow_cells","display_name":"Find Noflow Cells","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"interior_only","description":"Interior Only","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"find_parallel_flow","display_name":"Find Parallel Flow","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pointer","description":"D8 Pointer","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"hydrologic_connectivity","display_name":"Hydrologic Connectivity","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"exponent","description":"Exponent","type":"float","required":false,"default":1.1,"options":[],"kind":"double"},{"name":"convergence_threshold","description":"Convergence Threshold","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output1","description":"Output1","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output2","description":"Output2","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"relative_stream_power_index","display_name":"Relative Stream Power Index","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"specific_catchment_area","description":"Specific Catchment Area","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"slope","description":"Slope","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"exponent","description":"Exponent","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"sediment_transport_index","display_name":"Sediment Transport Index","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"sca","description":"Sca","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"slope","description":"Slope","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"sca_exponent","description":"Sca Exponent","type":"float","required":false,"default":0.4,"options":[],"kind":"double"},{"name":"slope_exponent","description":"Slope Exponent","type":"float","required":false,"default":1.3,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"wetness_index","display_name":"Wetness Index","summary":"","category":"Hydrology - Hydrologic Indices","taxonomy_category":"hydrology","taxonomy_subcategory":"hydrologic_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"sca","description":"Sca","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"slope","description":"Slope","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"extract_streams","display_name":"Extract Streams","summary":"","category":"Hydrology - Streams - Network Extraction","taxonomy_category":"streams","taxonomy_subcategory":"network_extraction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"flow_accumulation","description":"Flow Accumulation","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"threshold","description":"Threshold","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"extract_valleys","display_name":"Extract Valleys","summary":"","category":"Hydrology - Streams - Network Extraction","taxonomy_category":"streams","taxonomy_subcategory":"network_extraction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"variant","description":"Variant","type":"str","required":false,"default":"lq","options":[],"kind":"string"},{"name":"line_thin","description":"Line Thin","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"prune_vector_streams","display_name":"Prune Vector Streams","summary":"","category":"Hydrology - Streams - Network Extraction","taxonomy_category":"streams","taxonomy_subcategory":"network_extraction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"streams","description":"Streams","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"threshold","description":"Threshold","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"snap_distance","description":"Snap Distance","type":"float","required":false,"default":0.001,"options":[],"kind":"double"},{"name":"max_ridge_cutting_height","description":"Max Ridge Cutting Height","type":"float","required":false,"default":10,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"raster_streams_to_vector","display_name":"Raster Streams To Vector","summary":"","category":"Hydrology - Streams - Network Extraction","taxonomy_category":"streams","taxonomy_subcategory":"network_extraction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Vector"},{"id":"rasterize_streams","display_name":"Rasterize Streams","summary":"","category":"Hydrology - Streams - Network Extraction","taxonomy_category":"streams","taxonomy_subcategory":"network_extraction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"streams","description":"Streams","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"base","description":"Base","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"nodata","description":"Nodata","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"feature_id","description":"Feature Id","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"remove_short_streams","display_name":"Remove Short Streams","summary":"","category":"Hydrology - Streams - Network Extraction","taxonomy_category":"streams","taxonomy_subcategory":"network_extraction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_length","description":"Min Length","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"repair_stream_vector_topology","display_name":"Repair Stream Vector Topology","summary":"","category":"Hydrology - Streams - Network Extraction","taxonomy_category":"streams","taxonomy_subcategory":"network_extraction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"dist","description":"Dist","type":"float | str","required":false,"default":"","options":[],"kind":"double"}],"return_type":"Vector"},{"id":"river_centerlines","display_name":"River Centerlines","summary":"","category":"Hydrology - Streams - Network Extraction","taxonomy_category":"streams","taxonomy_subcategory":"network_extraction","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"raster","description":"Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_length","description":"Min Length","type":"int","required":false,"default":3,"options":[],"kind":"int"},{"name":"search_radius","description":"Search Radius","type":"int","required":false,"default":9,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"farthest_channel_head","display_name":"Farthest Channel Head","summary":"","category":"Hydrology - Streams - Longitudinal Analysis","taxonomy_category":"streams","taxonomy_subcategory":"longitudinal_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pointer","description":"D8 Pointer","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams_raster","description":"Streams Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"esri_pointer","description":"Esri Pointer","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"find_main_stem","display_name":"Find Main Stem","summary":"","category":"Hydrology - Streams - Longitudinal Analysis","taxonomy_category":"streams","taxonomy_subcategory":"longitudinal_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pointer","description":"D8 Pointer","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams_raster","description":"Streams Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"esri_pointer","description":"Esri Pointer","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"length_of_upstream_channels","display_name":"Length Of Upstream Channels","summary":"","category":"Hydrology - Streams - Longitudinal Analysis","taxonomy_category":"streams","taxonomy_subcategory":"longitudinal_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"long_profile","display_name":"Long Profile","summary":"","category":"Hydrology - Streams - Longitudinal Analysis","taxonomy_category":"streams","taxonomy_subcategory":"longitudinal_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pointer","description":"D8 Pointer","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams_raster","description":"Streams Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output_html_file","description":"Output Html File","type":"str","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"esri_pointer","description":"Esri Pointer","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"str | None"},{"id":"long_profile_from_points","display_name":"Long Profile From Points","summary":"","category":"Hydrology - Streams - Longitudinal Analysis","taxonomy_category":"streams","taxonomy_subcategory":"longitudinal_analysis","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pointer","description":"D8 Pointer","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output_html_file","description":"Output Html File","type":"str","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"esri_pointer","description":"Esri Pointer","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"str | None"},{"id":"hack_stream_order","display_name":"Hack Stream Order","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"horton_ratios","display_name":"Horton Ratios","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"tuple[float, float, float, float, str | None]"},{"id":"horton_stream_order","display_name":"Horton Stream Order","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"shreve_stream_magnitude","display_name":"Shreve Stream Magnitude","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"strahler_order_basins","display_name":"Strahler Order Basins","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"strahler_stream_order","display_name":"Strahler Stream Order","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"stream_link_class","display_name":"Stream Link Class","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"stream_link_identifier","display_name":"Stream Link Identifier","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"stream_link_length","display_name":"Stream Link Length","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"linkid","description":"Linkid","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"stream_link_slope","display_name":"Stream Link Slope","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"linkid","description":"Linkid","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"stream_slope_continuous","display_name":"Stream Slope Continuous","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"topological_stream_order","display_name":"Topological Stream Order","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"tributary_identifier","display_name":"Tributary Identifier","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"d8_pntr","description":"D8 Pntr","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"esri_pntr","description":"Esri Pntr","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"}],"return_type":"Raster"},{"id":"vector_stream_network_analysis","display_name":"Vector Stream Network Analysis","summary":"","category":"Hydrology - Streams - Ordering Metrics","taxonomy_category":"streams","taxonomy_subcategory":"ordering_metrics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"streams","description":"Streams","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"cutting_height","description":"Cutting Height","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"snap","description":"Snap","type":"float","required":false,"default":0.1,"options":[],"kind":"double"}],"return_type":"Vector"},{"id":"add_point_coordinates_to_table","display_name":"Add Point Coordinates To Table","summary":"","category":"Conversion - Vector & Table I/O","taxonomy_category":"conversion","taxonomy_subcategory":"vector_table_io","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"clean_vector","display_name":"Clean Vector","summary":"","category":"Conversion - Vector & Table I/O","taxonomy_category":"conversion","taxonomy_subcategory":"vector_table_io","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"csv_points_to_vector","display_name":"Csv Points To Vector","summary":"","category":"Conversion - Vector & Table I/O","taxonomy_category":"conversion","taxonomy_subcategory":"vector_table_io","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_file","description":"Input File","type":"str","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"x_field_num","description":"X Field Num","type":"int","required":false,"default":0,"options":[],"kind":"int"},{"name":"y_field_num","description":"Y Field Num","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"epsg","description":"Epsg","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"export_table_to_csv","display_name":"Export Table To Csv","summary":"","category":"Conversion - Vector & Table I/O","taxonomy_category":"conversion","taxonomy_subcategory":"vector_table_io","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output_csv_file","description":"Output Csv File","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"headers","description":"Headers","type":"bool","required":false,"default":true,"options":[],"kind":"bool"}],"return_type":"str"},{"id":"join_tables","display_name":"Join Tables","summary":"","category":"Conversion - Vector & Table I/O","taxonomy_category":"conversion","taxonomy_subcategory":"vector_table_io","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"primary_vector","description":"Primary Vector","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"primary_key_field","description":"Primary Key Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"foreign_vector","description":"Foreign Vector","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"foreign_key_field","description":"Foreign Key Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"import_field","description":"Import Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"merge_table_with_csv","display_name":"Merge Table With Csv","summary":"","category":"Conversion - Vector & Table I/O","taxonomy_category":"conversion","taxonomy_subcategory":"vector_table_io","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"primary_vector","description":"Primary Vector","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"primary_key_field","description":"Primary Key Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"foreign_csv_filename","description":"Foreign Csv Filename","type":"str","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"foreign_key_field","description":"Foreign Key Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"import_field","description":"Import Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"merge_vectors","display_name":"Merge Vectors","summary":"","category":"Conversion - Vector & Table I/O","taxonomy_category":"conversion","taxonomy_subcategory":"vector_table_io","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Vector | str]","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"reinitialize_attribute_table","display_name":"Reinitialize Attribute Table","summary":"","category":"Conversion - Vector & Table I/O","taxonomy_category":"conversion","taxonomy_subcategory":"vector_table_io","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"vector_summary_statistics","display_name":"Vector Summary Statistics","summary":"","category":"Conversion - Vector & Table I/O","taxonomy_category":"conversion","taxonomy_subcategory":"vector_table_io","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"group_field","description":"Group Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"value_field","description":"Value Field","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"fix_dangling_arcs","display_name":"Fix Dangling Arcs","summary":"","category":"Conversion - Geometry & Topology","taxonomy_category":"conversion","taxonomy_subcategory":"geometry_topology","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"snap_dist","description":"Snap Dist","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"lines_to_polygons","display_name":"Lines To Polygons","summary":"","category":"Conversion - Geometry & Topology","taxonomy_category":"conversion","taxonomy_subcategory":"geometry_topology","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"multipart_to_singlepart","display_name":"Multipart To Singlepart","summary":"","category":"Conversion - Geometry & Topology","taxonomy_category":"conversion","taxonomy_subcategory":"geometry_topology","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"exclude_holes","description":"Exclude Holes","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"polygons_to_lines","display_name":"Polygons To Lines","summary":"","category":"Conversion - Geometry & Topology","taxonomy_category":"conversion","taxonomy_subcategory":"geometry_topology","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"remove_polygon_holes","display_name":"Remove Polygon Holes","summary":"","category":"Conversion - Geometry & Topology","taxonomy_category":"conversion","taxonomy_subcategory":"geometry_topology","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"singlepart_to_multipart","display_name":"Singlepart To Multipart","summary":"","category":"Conversion - Geometry & Topology","taxonomy_category":"conversion","taxonomy_subcategory":"geometry_topology","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field","description":"Field","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"topology_rule_autofix","display_name":"Topology Rule Autofix","summary":"","category":"Conversion - Geometry & Topology","taxonomy_category":"conversion","taxonomy_subcategory":"geometry_topology","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"rule_set","description":"Rule configuration as JSON array/object, CSV string, or file path. Applies fixes for supported rules only: line_endpoints_must_snap_within_tolerance, point_must_be_covered_by_line, polygon_must_not_have_gaps, line_must_not_have_dangles.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"snap_tolerance","description":"Tolerance for snapping operations in coordinate units. Defaults to 0.01.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"dry_run","description":"If true (default), emits change report without modifying input. If false, applies changes.","type":"bool","required":false,"default":null,"options":[],"kind":"bool"},{"name":"output","description":"Output vector path for fixed features. If omitted, derived beside input.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_out"},{"name":"change_report","description":"Optional JSON change audit-trail report path.","type":"file","required":false,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"topology_rule_validate","display_name":"Topology Rule Validate","summary":"","category":"Conversion - Geometry & Topology","taxonomy_category":"conversion","taxonomy_subcategory":"geometry_topology","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"rule_set","description":"Rule configuration as JSON array/object, CSV string, or file path. Defaults to all 6 MVP rules. Supported: line_must_not_self_intersect, polygon_must_not_overlap, polygon_must_not_have_gaps, line_must_not_have_dangles, point_must_be_covered_by_line, line_endpoints_must_snap_within_tolerance.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"snap_tolerance","description":"Tolerance for line_endpoints_must_snap_within_tolerance rule in coordinate units. Defaults to 1.0.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output vector path for violations. If omitted, derived beside input.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_out"},{"name":"report","description":"Optional JSON summary report path.","type":"file","required":false,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"topology_validation_report","display_name":"Topology Validation Report","summary":"","category":"Conversion - Geometry & Topology","taxonomy_category":"conversion","taxonomy_subcategory":"geometry_topology","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"convert_nodata_to_zero","display_name":"Convert Nodata To Zero","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"modify_nodata_value","display_name":"Modify Nodata Value","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"new_value","description":"New Value","type":"float","required":false,"default":-32768,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"new_raster_from_base_raster","display_name":"New Raster From Base Raster","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base","description":"Base","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"out_val","description":"Out Val","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"data_type","description":"Data Type","type":"str","required":false,"default":"float","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"new_raster_from_base_vector","display_name":"New Raster From Base Vector","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base","description":"Base","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"cell_size","description":"Cell Size","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"out_val","description":"Out Val","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"data_type","description":"Data Type","type":"Literal[\"integer\", \"float\", \"double\"]","required":false,"default":"float","options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"raster_to_vector_lines","display_name":"Raster To Vector Lines","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"raster_to_vector_points","display_name":"Raster To Vector Points","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"raster_to_vector_polygons","display_name":"Raster To Vector Polygons","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"remove_raster_polygon_holes","display_name":"Remove Raster Polygon Holes","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"threshold_size","description":"Threshold Size","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"use_diagonals","description":"Use Diagonals","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"set_nodata_value","display_name":"Set Nodata Value","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"back_value","description":"Back Value","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"vector_lines_to_raster","display_name":"Vector Lines To Raster","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"field_name","description":"Field Name","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"cell_size","description":"Cell Size","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"vector_points_to_raster","display_name":"Vector Points To Raster","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"field_name","description":"Field Name","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"assign_op","description":"Assign Op","type":"str","required":false,"default":"last","options":[],"kind":"string"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"cell_size","description":"Cell Size","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"vector_polygons_to_raster","display_name":"Vector Polygons To Raster","summary":"","category":"Conversion - Raster/Vector Conversion","taxonomy_category":"conversion","taxonomy_subcategory":"raster_vector_conversion","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Vector","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"field_name","description":"Field Name","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"cell_size","description":"Cell Size","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"local_morans_i_lisa_raster","display_name":"Local Morans I Lisa Raster","summary":"","category":"Raster - Spatial Statistics","taxonomy_category":"raster","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer with observation points.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field","description":"Numeric attribute field to analyze.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"weights_mode","description":"Neighborhood mode: queen, rook, k_nearest, distance_band.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"k","description":"k value for k_nearest mode (default 8).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"distance","description":"Distance threshold for distance_band mode.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"row_standardize","description":"Apply row standardization to weights (default true).","type":"lidar","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"island_policy","description":"Island handling: drop_with_warning, keep_zero_weight, error.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"alpha","description":"Significance threshold in [0, 1]; default 0.05.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Output raster cell size (optional; auto-computed from extent).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output raster path (classification: 0=NS, 1=HH, 2=LL, 3=HL, 4=LH).","type":"raster","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"getis_ord_gi_star_raster","display_name":"Getis Ord Gi Star Raster","summary":"","category":"Raster - Spatial Statistics","taxonomy_category":"raster","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer with observation points.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field","description":"Numeric attribute field to analyze.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"weights_mode","description":"Neighborhood mode: queen, rook, k_nearest, distance_band.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"k","description":"k value for k_nearest mode (default 8).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"distance","description":"Distance threshold for distance_band mode.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"row_standardize","description":"Apply row standardization to weights (default true).","type":"lidar","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"island_policy","description":"Island handling: drop_with_warning, keep_zero_weight, error.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"alpha","description":"Significance threshold in [0, 1]; default 0.05.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Output raster cell size (optional; auto-computed from extent).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output raster path (classification: -1=Cold, 0=NS, 1=Hot).","type":"raster","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"ordinary_kriging","display_name":"Ordinary Kriging","summary":"","category":"Raster - Spatial Statistics","taxonomy_category":"raster","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Input points vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"value_field","description":"Numeric attribute field containing values to interpolate.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Output cell size in map units (required if no base_raster).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"base_raster","description":"Optional base raster controlling output geometry.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Optional output raster path.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"local_kriging","display_name":"Local Kriging","summary":"","category":"Raster - Spatial Statistics","taxonomy_category":"raster","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Input points vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"value_field","description":"Numeric attribute field containing values to interpolate.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"k_neighbors","description":"Number of nearest neighbors to use; defaults to 10.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Output cell size in map units (required if no base_raster).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"base_raster","description":"Optional base raster controlling output geometry.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Optional output raster path.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"simple_kriging","display_name":"Simple Kriging","summary":"","category":"Raster - Spatial Statistics","taxonomy_category":"raster","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Input points vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"value_field","description":"Numeric attribute field containing values to interpolate.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"known_mean","description":"Known mean of the spatial field.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Output cell size in map units (required if no base_raster).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"base_raster","description":"Optional base raster controlling output geometry.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Optional output raster path.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"universal_kriging","display_name":"Universal Kriging","summary":"","category":"Raster - Spatial Statistics","taxonomy_category":"raster","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Input points vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"value_field","description":"Numeric attribute field containing values to interpolate.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"trend_order","description":"Polynomial trend order (1=linear, 2=quadratic); defaults to 1.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Output cell size in map units (required if no base_raster).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"base_raster","description":"Optional base raster controlling output geometry.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Optional output raster path.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"spacetime_kriging","display_name":"Spacetime Kriging","summary":"","category":"Raster - Spatial Statistics","taxonomy_category":"raster","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Input points vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"value_field","description":"Numeric attribute field containing values to interpolate.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"time_field","description":"Temporal attribute field containing time values.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Output cell size in map units (required if no base_raster).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"base_raster","description":"Optional base raster controlling output geometry.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Optional output raster path.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"spatial_lag_regression_raster","display_name":"Spatial Lag Regression Raster","summary":"","category":"Raster - Spatial Statistics","taxonomy_category":"raster","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"response_field","description":"Response variable.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"predictor_fields","description":"Predictor fields.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"weights_mode","description":"Neighborhood mode.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"k","description":"k for k_nearest.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"distance","description":"Distance threshold.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"row_standardize","description":"Row standardize weights.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Output raster cell size.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output raster.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"spatial_error_regression_raster","display_name":"Spatial Error Regression Raster","summary":"","category":"Raster - Spatial Statistics","taxonomy_category":"raster","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"response_field","description":"Response variable.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"predictor_fields","description":"Predictor fields.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"weights_mode","description":"Neighborhood mode.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"k","description":"k for k_nearest.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"distance","description":"Distance threshold.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"row_standardize","description":"Row standardize weights.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Output raster cell size.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output raster.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"geographically_weighted_regression_raster","display_name":"Geographically Weighted Regression Raster","summary":"","category":"Raster - Spatial Statistics","taxonomy_category":"raster","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"response_field","description":"Response variable.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"predictor_fields","description":"Predictor fields.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"bandwidth","description":"Kernel bandwidth.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"kernel","description":"Kernel type.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Output raster cell size.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output_prefix","description":"Output raster prefix.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"inhomogeneous_intensity_raster","display_name":"Inhomogeneous Intensity Raster","summary":"","category":"Raster - Spatial Statistics","taxonomy_category":"raster","taxonomy_subcategory":"spatial_statistics","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input point vector layer.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"bandwidth","description":"Kernel bandwidth.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"kernel","description":"Kernel type.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Output raster cell size.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output intensity raster.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"add","display_name":"Add","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"average_overlay","display_name":"Average Overlay","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"bool_and","display_name":"Bool And","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"bool_not","display_name":"Bool Not","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"bool_or","display_name":"Bool Or","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"bool_xor","display_name":"Bool Xor","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"count_if","display_name":"Count If","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"comparison_value","description":"Comparison Value","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"divide","display_name":"Divide","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"highest_position","display_name":"Highest Position","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"lowest_position","display_name":"Lowest Position","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"max_absolute_overlay","display_name":"Max Absolute Overlay","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"max_overlay","display_name":"Max Overlay","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"min_absolute_overlay","display_name":"Min Absolute Overlay","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"min_overlay","display_name":"Min Overlay","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"modulo","display_name":"Modulo","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"multiply","display_name":"Multiply","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"multiply_overlay","display_name":"Multiply Overlay","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"percent_equal_to","display_name":"Percent Equal To","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"comparison","description":"Comparison","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"percent_greater_than","display_name":"Percent Greater Than","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"comparison","description":"Comparison","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"percent_less_than","display_name":"Percent Less Than","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"comparison","description":"Comparison","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"pick_from_list","display_name":"Pick From List","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"pos_input","description":"Pos Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"power","display_name":"Power","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str | float | int","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"standard_deviation_overlay","display_name":"Standard Deviation Overlay","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"subtract","display_name":"Subtract","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"sum_overlay","display_name":"Sum Overlay","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"update_nodata_cells","display_name":"Update Nodata Cells","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"weighted_overlay","display_name":"Weighted Overlay","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"factors","description":"Factors","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"weights","description":"Weights","type":"list[float]","required":true,"default":null,"options":[],"kind":"double"},{"name":"cost","description":"Cost","type":"list[bool] | None","required":false,"default":null,"options":[],"kind":"bool"},{"name":"constraints","description":"Constraints","type":"list[Raster | str] | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"scale_max","description":"Scale Max","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"weighted_sum","display_name":"Weighted Sum","summary":"","category":"Raster - Overlay Math","taxonomy_category":"raster","taxonomy_subcategory":"overlay_math","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"weights","description":"Weights","type":"list[float]","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"image_correlation_neighbourhood_analysis","display_name":"Image Correlation Neighbourhood Analysis","summary":"","category":"Raster - Local Neighborhood","taxonomy_category":"raster","taxonomy_subcategory":"local_neighborhood","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"correlation_stat","description":"Correlation Stat","type":"str","required":false,"default":"pearson","options":[],"kind":"string"},{"name":"output1","description":"Output1","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output2","description":"Output2","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"natural_neighbour_interpolation","display_name":"Natural Neighbour Interpolation","summary":"","category":"Raster - Local Neighborhood","taxonomy_category":"raster","taxonomy_subcategory":"local_neighborhood","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":false,"default":"FID","options":[],"kind":"string"},{"name":"use_z","description":"Use Z","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"cell_size","description":"Cell Size","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"clip_to_hull","description":"Clip To Hull","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"nearest_neighbour_interpolation","display_name":"Nearest Neighbour Interpolation","summary":"","category":"Raster - Local Neighborhood","taxonomy_category":"raster","taxonomy_subcategory":"local_neighborhood","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":false,"default":"FID","options":[],"kind":"string"},{"name":"use_z","description":"Use Z","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"cell_size","description":"Cell Size","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"conditional_evaluation","display_name":"Conditional Evaluation","summary":"","category":"Raster - Reclassify & Mask","taxonomy_category":"raster","taxonomy_subcategory":"reclass_mask","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"statement","description":"Statement","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"true_value","description":"True Value","type":"Raster | str | float | int | str | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"false_value","description":"False Value","type":"Raster | str | float | int | str | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"reclass","display_name":"Reclass","summary":"","category":"Raster - Reclassify & Mask","taxonomy_category":"raster","taxonomy_subcategory":"reclass_mask","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"reclass_values","description":"Reclass Values","type":"list[list[float]]","required":true,"default":null,"options":[],"kind":"double"},{"name":"assign_mode","description":"Assign Mode","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"reclass_equal_interval","display_name":"Reclass Equal Interval","summary":"","category":"Raster - Reclassify & Mask","taxonomy_category":"raster","taxonomy_subcategory":"reclass_mask","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"interval_size","description":"Interval Size","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"start_value","description":"Start Value","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"end_value","description":"End Value","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"buffer_raster","display_name":"Buffer Raster","summary":"","category":"Raster - Distance & Cost","taxonomy_category":"raster","taxonomy_subcategory":"distance_cost","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"buffer_size","description":"Buffer Size","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"grid_cell_units","description":"Grid Cell Units","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"cost_allocation","display_name":"Cost Allocation","summary":"","category":"Raster - Distance & Cost","taxonomy_category":"raster","taxonomy_subcategory":"distance_cost","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"source","description":"Source","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"backlink","description":"Backlink","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"cost_distance","display_name":"Cost Distance","summary":"","category":"Raster - Distance & Cost","taxonomy_category":"raster","taxonomy_subcategory":"distance_cost","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"source","description":"Source","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"cost","description":"Cost","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"backlink_output","description":"Backlink Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"tuple[Raster, Raster]"},{"id":"cost_pathway","display_name":"Cost Pathway","summary":"","category":"Raster - Distance & Cost","taxonomy_category":"raster","taxonomy_subcategory":"distance_cost","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"destination","description":"Destination","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"backlink","description":"Backlink","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"euclidean_allocation","display_name":"Euclidean Allocation","summary":"","category":"Raster - Distance & Cost","taxonomy_category":"raster","taxonomy_subcategory":"distance_cost","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"euclidean_distance","display_name":"Euclidean Distance","summary":"","category":"Raster - Distance & Cost","taxonomy_category":"raster","taxonomy_subcategory":"distance_cost","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"abs","display_name":"Abs","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"aggregate_raster","display_name":"Aggregate Raster","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"aggregation_factor","description":"Aggregation Factor","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"aggregation_type","description":"Aggregation Type","type":"str","required":false,"default":"mean","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"anova","display_name":"Anova","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"features","description":"Features","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"arccos","display_name":"Arccos","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"arcosh","display_name":"Arcosh","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"arcsin","display_name":"Arcsin","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"arctan","display_name":"Arctan","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"arsinh","display_name":"Arsinh","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"artanh","display_name":"Artanh","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"atan2","display_name":"Atan2","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str | float | int","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"block_maximum","display_name":"Block Maximum","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":false,"default":"FID","options":[],"kind":"string"},{"name":"use_z","description":"Use Z","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"cell_size","description":"Cell Size","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"block_minimum","display_name":"Block Minimum","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":false,"default":"FID","options":[],"kind":"string"},{"name":"use_z","description":"Use Z","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"cell_size","description":"Cell Size","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"boundary_shape_complexity","display_name":"Boundary Shape Complexity","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"ceil","display_name":"Ceil","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"centroid_raster","display_name":"Centroid Raster","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, str]"},{"id":"clip_raster_to_polygon","display_name":"Clip Raster To Polygon","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"polygons","description":"Polygons","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"maintain_dimensions","description":"Maintain Dimensions","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"clump","display_name":"Clump","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"diag","description":"Diag","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"cos","display_name":"Cos","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"cosh","display_name":"Cosh","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"create_plane","display_name":"Create Plane","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base","description":"Base","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"gradient","description":"Gradient","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"aspect","description":"Aspect","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"constant","description":"Constant","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"crispness_index","display_name":"Crispness Index","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"cross_tabulation","display_name":"Cross Tabulation","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"cumulative_distribution","display_name":"Cumulative Distribution","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"dbscan","display_name":"Dbscan","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"search_distance","description":"Search Distance","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"min_points","description":"Min Points","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, str]"},{"id":"decrement","display_name":"Decrement","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"value","description":"Value","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"edge_proportion","display_name":"Edge Proportion","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"equal_to","display_name":"Equal To","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"erase_polygon_from_raster","display_name":"Erase Polygon From Raster","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"polygons","description":"Polygons","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"exp","display_name":"Exp","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"exp2","display_name":"Exp2","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"fft_random_field","display_name":"Fft Random Field","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base_raster","description":"Base Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"range","description":"Range","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"filter_raster_features_by_area","display_name":"Filter Raster Features By Area","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"threshold","description":"Threshold","type":"int","required":true,"default":null,"options":[],"kind":"int"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"find_patch_edge_cells","display_name":"Find Patch Edge Cells","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"floor","display_name":"Floor","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"greater_than","display_name":"Greater Than","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"heat_map","display_name":"Heat Map","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"bandwidth","description":"Bandwidth","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"field_name","description":"Field Name","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"cell_size","description":"Cell Size","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"kernel_function","description":"Kernel Function","type":"str","required":false,"default":"quartic","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"idw_interpolation","display_name":"Idw Interpolation","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":false,"default":"FID","options":[],"kind":"string"},{"name":"use_z","description":"Use Z","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"weight","description":"Weight","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"radius","description":"Radius","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"min_points","description":"Min Points","type":"int","required":false,"default":0,"options":[],"kind":"int"},{"name":"cell_size","description":"Cell Size","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"image_autocorrelation","display_name":"Image Autocorrelation","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"contiguity","description":"Contiguity","type":"Literal[\"rook\", \"king\", \"queen\", \"bishop\"]","required":false,"default":"rook","options":[],"kind":"string"}],"return_type":"str"},{"id":"image_correlation","display_name":"Image Correlation","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"}],"return_type":"str"},{"id":"image_regression","display_name":"Image Regression","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"independent_variable","description":"Independent Variable","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"dependent_variable","description":"Dependent Variable","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"standardize_residuals","description":"Standardize Residuals","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, str]"},{"id":"increment","display_name":"Increment","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"value","description":"Value","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"inplace_add","display_name":"Inplace Add","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str | float | int","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"inplace_divide","display_name":"Inplace Divide","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str | float | int","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"inplace_multiply","display_name":"Inplace Multiply","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str | float | int","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"inplace_subtract","display_name":"Inplace Subtract","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str | float | int","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"integer_division","display_name":"Integer Division","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"inverse_pca","display_name":"Inverse Pca","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"component_rasters","description":"Component Rasters","type":"list[Raster]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"pca_report","description":"Pca Report","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"list[Raster]"},{"id":"is_nodata","display_name":"Is Nodata","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"kappa_index","display_name":"Kappa Index","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"}],"return_type":"str"},{"id":"ks_normality_test","display_name":"Ks Normality Test","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"num_samples","description":"Num Samples","type":"int | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"str"},{"id":"less_than","display_name":"Less Than","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"list_unique_values_raster","display_name":"List Unique Values Raster","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"max_values","description":"Max Values","type":"int","required":false,"default":10000,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"ln","display_name":"Ln","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"log10","display_name":"Log10","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"log2","display_name":"Log2","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"map_features","display_name":"Map Features","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_feature_height","description":"Min Feature Height","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"min_feature_size","description":"Min Feature Size","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"max","display_name":"Max","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str | float | int","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"min","display_name":"Min","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str | float | int","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"modified_shepard_interpolation","display_name":"Modified Shepard Interpolation","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":false,"default":"FID","options":[],"kind":"string"},{"name":"use_z","description":"Use Z","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"weight","description":"Weight","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"radius","description":"Radius","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"min_points","description":"Min Points","type":"int","required":false,"default":8,"options":[],"kind":"int"},{"name":"use_quadratic_basis","description":"Use Quadratic Basis","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"cell_size","description":"Cell Size","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"use_data_hull","description":"Use Data Hull","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"narrowness_index","display_name":"Narrowness Index","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"negate","display_name":"Negate","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"nibble","display_name":"Nibble","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"mask","description":"Mask","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"use_nodata","description":"Use Nodata","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"nibble_nodata","description":"Nibble Nodata","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"not_equal_to","display_name":"Not Equal To","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"paired_sample_t_test","display_name":"Paired Sample T Test","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"num_samples","description":"Num Samples","type":"int | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"str"},{"id":"phi_coefficient","display_name":"Phi Coefficient","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"}],"return_type":"str"},{"id":"principal_component_analysis","display_name":"Principal Component Analysis","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"num_components","description":"Num Components","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"standardized","description":"Standardized","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"list[Raster]"},{"id":"print_geotiff_tags","display_name":"Print Geotiff Tags","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"}],"return_type":"str"},{"id":"quantiles","display_name":"Quantiles","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"num_quantiles","description":"Num Quantiles","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"radial_basis_function_interpolation","display_name":"Radial Basis Function Interpolation","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":false,"default":"FID","options":[],"kind":"string"},{"name":"use_z","description":"Use Z","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"radius","description":"Radius","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"min_points","description":"Min Points","type":"int","required":false,"default":16,"options":[],"kind":"int"},{"name":"cell_size","description":"Cell Size","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"func_type","description":"Func Type","type":"Literal[\"thinplatespline\", \"polyharmonic\", \"gaussian\", \"multiquadric\", \"inversemultiquadric\"]","required":false,"default":"thinplatespline","options":[],"kind":"string"},{"name":"poly_order","description":"Poly Order","type":"Literal[\"none\", \"constant\", \"quadratic\"]","required":false,"default":"none","options":[],"kind":"string"},{"name":"weight","description":"Weight","type":"float","required":false,"default":0.1,"options":[],"kind":"double"},{"name":"approximate_mode","description":"Approximate Mode","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"radius_of_gyration","display_name":"Radius Of Gyration","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"random_field","display_name":"Random Field","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base","description":"Base","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"random_forest_classification_fit","display_name":"Random Forest Classification Fit","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"class_field_name","description":"Class Field Name","type":"str","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"split_criterion","description":"Split Criterion","type":"str","required":false,"default":"gini","options":[],"kind":"string"},{"name":"n_trees","description":"N Trees","type":"int","required":false,"default":200,"options":[],"kind":"int"},{"name":"min_samples_leaf","description":"Min Samples Leaf","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"min_samples_split","description":"Min Samples Split","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"test_proportion","description":"Test Proportion","type":"float","required":false,"default":0.2,"options":[],"kind":"double"}],"return_type":"list[int]"},{"id":"random_forest_classification_predict","display_name":"Random Forest Classification Predict","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"model_bytes","description":"Model Bytes","type":"list[int]","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"random_forest_regression_fit","display_name":"Random Forest Regression Fit","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"training_data","description":"Training Data","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"scaling_method","description":"Scaling Method","type":"str","required":false,"default":"none","options":[],"kind":"string"},{"name":"n_trees","description":"N Trees","type":"int","required":false,"default":200,"options":[],"kind":"int"},{"name":"min_samples_leaf","description":"Min Samples Leaf","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"min_samples_split","description":"Min Samples Split","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"test_proportion","description":"Test Proportion","type":"float","required":false,"default":0.2,"options":[],"kind":"double"}],"return_type":"list[int]"},{"id":"random_forest_regression_predict","display_name":"Random Forest Regression Predict","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"model_bytes","description":"Model Bytes","type":"list[int]","required":true,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"random_sample","display_name":"Random Sample","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base","description":"Base","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"num_samples","description":"Num Samples","type":"int","required":false,"default":1000,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"raster_area","display_name":"Raster Area","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"units","description":"Units","type":"str","required":false,"default":"map units","options":["degrees","radians","percent"],"kind":"enum"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"raster_calculator","display_name":"Raster Calculator","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"expression","description":"Expression","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"input_rasters","description":"Input Rasters","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"raster_cell_assignment","display_name":"Raster Cell Assignment","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"what_to_assign","description":"What To Assign","type":"Literal[\"column\", \"row\", \"x\", \"y\"]","required":false,"default":"column","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"raster_histogram","display_name":"Raster Histogram","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"bins","description":"Bins","type":"int","required":false,"default":256,"options":[],"kind":"int"}],"return_type":"str"},{"id":"raster_perimeter","display_name":"Raster Perimeter","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"units","description":"Units","type":"str","required":false,"default":"map units","options":["degrees","radians","percent"],"kind":"enum"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"raster_summary_stats","display_name":"Raster Summary Stats","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"}],"return_type":"str"},{"id":"reciprocal","display_name":"Reciprocal","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"rescale_value_range","display_name":"Rescale Value Range","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"out_min","description":"Out Min","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"out_max","description":"Out Max","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"clip_min","description":"Clip Min","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"clip_max","description":"Clip Max","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"root_mean_square_error","display_name":"Root Mean Square Error","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"base","description":"Base","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"}],"return_type":"str"},{"id":"round","display_name":"Round","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"shape_complexity_index_raster","display_name":"Shape Complexity Index Raster","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"sieve","display_name":"Sieve","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"threshold","description":"Threshold","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"zero_background","description":"Zero Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"sin","display_name":"Sin","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"sinh","display_name":"Sinh","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"sqrt","display_name":"Sqrt","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"square","display_name":"Square","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"tan","display_name":"Tan","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"tanh","display_name":"Tanh","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"tin_interpolation","display_name":"Tin Interpolation","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"field_name","description":"Field Name","type":"str","required":false,"default":"FID","options":[],"kind":"string"},{"name":"use_z","description":"Use Z","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"cell_size","description":"Cell Size","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"base_raster","description":"Base Raster","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"max_triangle_edge_length","description":"Max Triangle Edge Length","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"to_degrees","display_name":"To Degrees","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"to_radians","display_name":"To Radians","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"trend_surface","display_name":"Trend Surface","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input_raster","description":"Input Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"polynomial_order","description":"Polynomial Order","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"trend_surface_vector_points","display_name":"Trend Surface Vector Points","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"vector_input","description":"Vector Input","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"cell_size","description":"Cell Size","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"field_name","description":"Field Name","type":"str","required":true,"default":null,"options":[],"kind":"string"},{"name":"polynomial_order","description":"Polynomial Order","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"truncate","display_name":"Truncate","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"band_mode","description":"Band Mode","type":"str","required":false,"default":"all","options":[],"kind":"string"},{"name":"bands","description":"Bands","type":"list[int] | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"Raster"},{"id":"turning_bands_simulation","display_name":"Turning Bands Simulation","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"base_raster","description":"Base Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"range","description":"Range","type":"float","required":true,"default":null,"options":[],"kind":"double"},{"name":"iterations","description":"Iterations","type":"int","required":false,"default":1000,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"two_sample_ks_test","display_name":"Two Sample Ks Test","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"num_samples","description":"Num Samples","type":"int | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"str"},{"id":"wilcoxon_signed_rank_test","display_name":"Wilcoxon Signed Rank Test","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input1","description":"Input1","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"input2","description":"Input2","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"num_samples","description":"Num Samples","type":"int | None","required":false,"default":null,"options":[],"kind":"int"}],"return_type":"str"},{"id":"z_scores","display_name":"Z Scores","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"zonal_statistics","display_name":"Zonal Statistics","summary":"","category":"Raster","taxonomy_category":"raster","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"data_raster","description":"Data Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"features_raster","description":"Features Raster","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"stat_type","description":"Stat Type","type":"str","required":false,"default":"mean","options":[],"kind":"string"},{"name":"zero_is_background","description":"Zero Is Background","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"assess_route","display_name":"Assess Route","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"routes","description":"Routes","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"segment_length","description":"Segment Length","type":"float","required":false,"default":100,"options":[],"kind":"double"},{"name":"search_radius","description":"Search Radius","type":"int","required":false,"default":15,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"breakline_mapping","display_name":"Breakline Mapping","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"threshold","description":"Threshold","type":"float","required":false,"default":0.8,"options":[],"kind":"double"},{"name":"min_length","description":"Min Length","type":"int","required":false,"default":3,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"convergence_index","display_name":"Convergence Index","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"dem_void_filling","display_name":"Dem Void Filling","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"fill","description":"Fill","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"mean_plane_dist","description":"Mean Plane Dist","type":"int","required":false,"default":20,"options":[],"kind":"int"},{"name":"edge_treatment","description":"Edge Treatment","type":"str","required":false,"default":"dem","options":[],"kind":"string"},{"name":"weight_value","description":"Weight Value","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"deviation_from_mean_elevation","display_name":"Deviation From Mean Elevation","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"difference_from_mean_elevation","display_name":"Difference From Mean Elevation","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"directional_relief","display_name":"Directional Relief","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"azimuth","description":"Azimuth","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"elev_above_pit","display_name":"Elev Above Pit","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"elev_above_pit_dist","display_name":"Elev Above Pit Dist","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"elevation_percentile","display_name":"Elevation Percentile","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"sig_digits","description":"Sig Digits","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"embankment_mapping","display_name":"Embankment Mapping","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"roads_vector","description":"Roads Vector","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"search_dist","description":"Search Dist","type":"float","required":false,"default":2.5,"options":[],"kind":"double"},{"name":"min_road_width","description":"Min Road Width","type":"float","required":false,"default":6,"options":[],"kind":"double"},{"name":"typical_embankment_width","description":"Typical Embankment Width","type":"float","required":false,"default":30,"options":[],"kind":"double"},{"name":"typical_embankment_max_height","description":"Typical Embankment Max Height","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"embankment_max_width","description":"Embankment Max Width","type":"float","required":false,"default":60,"options":[],"kind":"double"},{"name":"max_upwards_increment","description":"Max Upwards Increment","type":"float","required":false,"default":0.05,"options":[],"kind":"double"},{"name":"spillout_slope","description":"Spillout Slope","type":"float","required":false,"default":4,"options":[],"kind":"double"},{"name":"remove_embankments","description":"Remove Embankments","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_dem","description":"Output Dem","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster | None]"},{"id":"exposure_towards_wind_flux","display_name":"Exposure Towards Wind Flux","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"azimuth","description":"Azimuth","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"feature_preserving_smoothing","display_name":"Feature Preserving Smoothing","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"normal_diff_threshold","description":"Normal Diff Threshold","type":"float","required":false,"default":8,"options":[],"kind":"double"},{"name":"iterations","description":"Iterations","type":"int","required":false,"default":3,"options":[],"kind":"int"},{"name":"max_elevation_diff","description":"Max Elevation Diff","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"feature_preserving_smoothing_multiscale","display_name":"Feature Preserving Smoothing Multiscale","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input DEM raster path or typed raster object.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"smoothing_amount","description":"Overall smoothing amount in [0,1] (default 0.65). Higher values increase the diffusion budget, especially at coarse scales.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"edge_preservation","description":"Edge preservation strength in [0,1] (default 0.75). Higher values suppress smoothing across major breaks-in-slope more strongly.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"scale_levels","description":"Number of pyramid levels for coarse-to-fine smoothing (default 3).","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"fidelity","description":"Data-fidelity weight in screened Poisson reconstruction (default 0.45). Higher values keep the result closer to the source DEM.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"z_factor","description":"Optional z conversion factor (default 1.0).","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Optional output path. If omitted, result stays in memory.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"fetch_analysis","display_name":"Fetch Analysis","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"azimuth","description":"Azimuth","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"hgt_inc","description":"Hgt Inc","type":"float","required":false,"default":0.05,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"fill_missing_data","display_name":"Fill Missing Data","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"weight","description":"Weight","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"exclude_edge_nodata","description":"Exclude Edge Nodata","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"find_ridges","display_name":"Find Ridges","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"line_thin","description":"Line Thin","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"hillshade","display_name":"Hillshade","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"azimuth","description":"Azimuth","type":"float","required":false,"default":315,"options":[],"kind":"double"},{"name":"altitude","description":"Altitude","type":"float","required":false,"default":30,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"hypsometrically_tinted_hillshade","display_name":"Hypsometrically Tinted Hillshade","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"solar_altitude","description":"Solar Altitude","type":"float","required":false,"default":45,"options":[],"kind":"double"},{"name":"hillshade_weight","description":"Hillshade Weight","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"brightness","description":"Brightness","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"atmospheric_effects","description":"Atmospheric Effects","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"palette","description":"Palette","type":"str","required":false,"default":"atlas","options":[],"kind":"string"},{"name":"reverse_palette","description":"Reverse Palette","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"full_360_mode","description":"Full 360 Mode","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"local_hypsometric_analysis","display_name":"Local Hypsometric Analysis","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":4,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"num_steps","description":"Num Steps","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"step_nonlinearity","description":"Step Nonlinearity","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_scale","description":"Output Scale","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"low_points_on_headwater_divides","display_name":"Low Points On Headwater Divides","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Streams","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"max_downslope_elev_change","display_name":"Max Downslope Elev Change","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"max_upslope_elev_change","display_name":"Max Upslope Elev Change","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"min_downslope_elev_change","display_name":"Min Downslope Elev Change","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"multidirectional_hillshade","display_name":"Multidirectional Hillshade","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"altitude","description":"Altitude","type":"float","required":false,"default":30,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"full_360_mode","description":"Full 360 Mode","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"num_downslope_neighbours","display_name":"Num Downslope Neighbours","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"num_upslope_neighbours","display_name":"Num Upslope Neighbours","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"profile","display_name":"Profile","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"lines_vector","description":"Lines Vector","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"surface","description":"Surface","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"slope_vs_aspect_plot","display_name":"Slope Vs Aspect Plot","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"aspect_bin_size","description":"Aspect Bin Size","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"min_slope","description":"Min Slope","type":"float","required":false,"default":0.1,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"slope_vs_elev_plot","display_name":"Slope Vs Elev Plot","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"watershed","description":"Watershed","type":"list[Raster | str] | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"surface_area_ratio","display_name":"Surface Area Ratio","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"topographic_hachures","display_name":"Topographic Hachures","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"contour_interval","description":"Contour Interval","type":"float","required":false,"default":10,"options":[],"kind":"double"},{"name":"base_contour","description":"Base Contour","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"deflection_tolerance","description":"Deflection Tolerance","type":"float","required":false,"default":10,"options":[],"kind":"double"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":9,"options":[],"kind":"int"},{"name":"separation","description":"Separation","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"distmin","description":"Distmin","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"distmax","description":"Distmax","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"discretization","description":"Discretization","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"turnmax","description":"Turnmax","type":"float","required":false,"default":45,"options":[],"kind":"double"},{"name":"slopemin","description":"Slopemin","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"depth","description":"Depth","type":"int","required":false,"default":16,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"Vector"},{"id":"map_off_terrain_objects","display_name":"Map Off Terrain Objects","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"max_slope","description":"Max Slope","type":"float","required":false,"default":90,"options":[],"kind":"double"},{"name":"min_feature_size","description":"Min Feature Size","type":"int","required":false,"default":0,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"remove_off_terrain_objects","display_name":"Remove Off Terrain Objects","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"slope_threshold","description":"Slope Threshold","type":"float","required":false,"default":15,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"ridge_and_valley_vectors","display_name":"Ridge And Valley Vectors","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"ep_threshold","description":"Ep Threshold","type":"float","required":false,"default":30,"options":[],"kind":"double"},{"name":"slope_threshold","description":"Slope Threshold","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"min_length","description":"Min Length","type":"int","required":false,"default":20,"options":[],"kind":"int"},{"name":"output_ridges","description":"Output Ridges","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"output_valleys","description":"Output Valleys","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"}],"return_type":"tuple[Vector, Vector]"},{"id":"smooth_vegetation_residual","display_name":"Smooth Vegetation Residual","summary":"","category":"Terrain","taxonomy_category":"terrain","taxonomy_subcategory":"general","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"max_scale","description":"Max Scale","type":"int","required":false,"default":30,"options":[],"kind":"int"},{"name":"dev_threshold","description":"Dev Threshold","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"scale_threshold","description":"Scale Threshold","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"average_horizon_distance","display_name":"Average Horizon Distance","summary":"","category":"Terrain - Visibility","taxonomy_category":"terrain","taxonomy_subcategory":"visibility","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"az_fraction","description":"Az Fraction","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"observer_hgt_offset","description":"Observer Hgt Offset","type":"float","required":false,"default":0.05,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"horizon_angle","display_name":"Horizon Angle","summary":"","category":"Terrain - Visibility","taxonomy_category":"terrain","taxonomy_subcategory":"visibility","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"azimuth","description":"Azimuth","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"horizon_area","display_name":"Horizon Area","summary":"","category":"Terrain - Visibility","taxonomy_category":"terrain","taxonomy_subcategory":"visibility","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"az_fraction","description":"Az Fraction","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"observer_hgt_offset","description":"Observer Hgt Offset","type":"float","required":false,"default":0.05,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"openness","display_name":"Openness","summary":"","category":"Terrain - Visibility","taxonomy_category":"terrain","taxonomy_subcategory":"visibility","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"dist","description":"Dist","type":"int","required":false,"default":20,"options":[],"kind":"int"},{"name":"pos_output","description":"Pos Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"neg_output","description":"Neg Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"output","description":"Output raster","type":"str","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"shadow_animation","display_name":"Shadow Animation","summary":"","category":"Terrain - Visibility","taxonomy_category":"terrain","taxonomy_subcategory":"visibility","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"date","description":"Date","type":"str","required":false,"default":"21/06/2021","options":[],"kind":"string"},{"name":"time_interval","description":"Time Interval","type":"int","required":false,"default":30,"options":[],"kind":"int"},{"name":"location","description":"Location","type":"str","required":false,"default":"43.5448/-80.2482/-4","options":[],"kind":"string"},{"name":"palette","description":"Palette","type":"str","required":false,"default":"soft","options":[],"kind":"string"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"image_height","description":"Image Height","type":"int","required":false,"default":600,"options":[],"kind":"int"},{"name":"delay","description":"Delay","type":"int","required":false,"default":250,"options":[],"kind":"int"},{"name":"label","description":"Label","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"tuple[str, str]"},{"id":"shadow_image","display_name":"Shadow Image","summary":"","category":"Terrain - Visibility","taxonomy_category":"terrain","taxonomy_subcategory":"visibility","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"palette","description":"Palette","type":"str","required":false,"default":"soft","options":[],"kind":"string"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"date","description":"Date","type":"str","required":false,"default":"21/06/2021","options":[],"kind":"string"},{"name":"time","description":"Time","type":"str","required":false,"default":"13:00","options":[],"kind":"string"},{"name":"location","description":"Location","type":"str","required":false,"default":"43.5448/-80.2482/-4","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"sky_view_factor","display_name":"Sky View Factor","summary":"","category":"Terrain - Visibility","taxonomy_category":"terrain","taxonomy_subcategory":"visibility","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"az_fraction","description":"Az Fraction","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"observer_hgt_offset","description":"Observer Hgt Offset","type":"float","required":false,"default":0.05,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"skyline_analysis","display_name":"Skyline Analysis","summary":"","category":"Terrain - Visibility","taxonomy_category":"terrain","taxonomy_subcategory":"visibility","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"observer_hgt_offset","description":"Observer Hgt Offset","type":"float","required":false,"default":0.05,"options":[],"kind":"double"},{"name":"output_as_polygons","description":"Output As Polygons","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"az_fraction","description":"Az Fraction","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"report_path","description":"Report Path","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"tuple[Vector, str]"},{"id":"time_in_daylight","display_name":"Time In Daylight","summary":"","category":"Terrain - Visibility","taxonomy_category":"terrain","taxonomy_subcategory":"visibility","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"az_fraction","description":"Az Fraction","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"latitude","description":"Latitude","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"longitude","description":"Longitude","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"utc_offset","description":"Utc Offset","type":"str | None","required":false,"default":null,"options":[],"kind":"string"},{"name":"start_day","description":"Start Day","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"end_day","description":"End Day","type":"int","required":false,"default":365,"options":[],"kind":"int"},{"name":"start_time","description":"Start Time","type":"str","required":false,"default":"sunrise","options":[],"kind":"string"},{"name":"end_time","description":"End Time","type":"str","required":false,"default":"sunset","options":[],"kind":"string"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"viewshed","display_name":"Viewshed","summary":"","category":"Terrain - Visibility","taxonomy_category":"terrain","taxonomy_subcategory":"visibility","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"stations","description":"Stations","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"height","description":"Height","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"visibility_index","display_name":"Visibility Index","summary":"","category":"Terrain - Visibility","taxonomy_category":"terrain","taxonomy_subcategory":"visibility","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"station_height","description":"Station Height","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"resolution_factor","description":"Resolution Factor","type":"int","required":false,"default":8,"options":[],"kind":"int"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"accumulation_curvature","display_name":"Accumulation Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"aspect","display_name":"Aspect","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"casorati_curvature","display_name":"Casorati Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"curvedness","display_name":"Curvedness","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"difference_curvature","display_name":"Difference Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"gaussian_curvature","display_name":"Gaussian Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"generating_function","display_name":"Generating Function","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"horizontal_excess_curvature","display_name":"Horizontal Excess Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"maximal_curvature","display_name":"Maximal Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"mean_curvature","display_name":"Mean Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"minimal_curvature","display_name":"Minimal Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"plan_curvature","display_name":"Plan Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"principal_curvature_direction","display_name":"Principal Curvature Direction","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"profile_curvature","display_name":"Profile Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"relative_aspect","display_name":"Relative Aspect","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"azimuth","description":"Azimuth","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"ring_curvature","display_name":"Ring Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"rotor","display_name":"Rotor","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"shape_index","display_name":"Shape Index","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"slope","display_name":"Slope","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"units","description":"Units","type":"str","required":false,"default":"degrees","options":["degrees","radians","percent"],"kind":"enum"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"tangential_curvature","display_name":"Tangential Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"total_curvature","display_name":"Total Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"unsphericity","display_name":"Unsphericity","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"vertical_excess_curvature","display_name":"Vertical Excess Curvature","summary":"","category":"Terrain - Derivatives","taxonomy_category":"terrain","taxonomy_subcategory":"derivatives","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"average_normal_vector_angular_deviation","display_name":"Average Normal Vector Angular Deviation","summary":"","category":"Terrain - Roughness & Texture","taxonomy_category":"terrain","taxonomy_subcategory":"roughness_texture","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"circular_variance_of_aspect","display_name":"Circular Variance Of Aspect","summary":"","category":"Terrain - Roughness & Texture","taxonomy_category":"terrain","taxonomy_subcategory":"roughness_texture","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter","description":"Filter","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"edge_density","display_name":"Edge Density","summary":"","category":"Terrain - Roughness & Texture","taxonomy_category":"terrain","taxonomy_subcategory":"roughness_texture","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"norm_diff","description":"Norm Diff","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"ruggedness_index","display_name":"Ruggedness Index","summary":"","category":"Terrain - Roughness & Texture","taxonomy_category":"terrain","taxonomy_subcategory":"roughness_texture","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"spherical_std_dev_of_normals","display_name":"Spherical Std Dev Of Normals","summary":"","category":"Terrain - Roughness & Texture","taxonomy_category":"terrain","taxonomy_subcategory":"roughness_texture","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"standard_deviation_of_slope","display_name":"Standard Deviation Of Slope","summary":"","category":"Terrain - Roughness & Texture","taxonomy_category":"terrain","taxonomy_subcategory":"roughness_texture","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size","description":"Filter Size","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"elev_relative_to_min_max","display_name":"Elev Relative To Min Max","summary":"","category":"Terrain - Landform Indices","taxonomy_category":"terrain","taxonomy_subcategory":"landform_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"geomorphons","display_name":"Geomorphons","summary":"","category":"Terrain - Landform Indices","taxonomy_category":"terrain","taxonomy_subcategory":"landform_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"search_distance","description":"Search Distance","type":"int","required":false,"default":50,"options":[],"kind":"int"},{"name":"flatness_threshold","description":"Flatness Threshold","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"flatness_distance","description":"Flatness Distance","type":"int","required":false,"default":0,"options":[],"kind":"int"},{"name":"skip_distance","description":"Skip Distance","type":"int","required":false,"default":0,"options":[],"kind":"int"},{"name":"output_forms","description":"Output Forms","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"analyze_residuals","description":"Analyze Residuals","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"hypsometric_analysis","display_name":"Hypsometric Analysis","summary":"","category":"Terrain - Landform Indices","taxonomy_category":"terrain","taxonomy_subcategory":"landform_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"inputs","description":"Inputs","type":"list[Raster | str]","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"watershed","description":"Watershed","type":"list[Raster | str] | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"multiscale_topographic_position_class","display_name":"Multiscale Topographic Position Class","summary":"","category":"Terrain - Landform Indices","taxonomy_category":"terrain","taxonomy_subcategory":"landform_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"local_min_scale","description":"Local Min Scale","type":"int","required":false,"default":5,"options":[],"kind":"int"},{"name":"local_max_scale","description":"Local Max Scale","type":"int","required":false,"default":80,"options":[],"kind":"int"},{"name":"local_step_size","description":"Local Step Size","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"broad_min_scale","description":"Broad Min Scale","type":"int","required":false,"default":500,"options":[],"kind":"int"},{"name":"broad_max_scale","description":"Broad Max Scale","type":"int","required":false,"default":2000,"options":[],"kind":"int"},{"name":"broad_step_size","description":"Broad Step Size","type":"int","required":false,"default":20,"options":[],"kind":"int"},{"name":"local_threshold","description":"Local Threshold","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"broad_threshold","description":"Broad Threshold","type":"float","required":false,"default":0.5,"options":[],"kind":"double"},{"name":"min_patch_size","description":"Min Patch Size","type":"int","required":false,"default":0,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_confidence","description":"Output Confidence","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"pennock_landform_classification","display_name":"Pennock Landform Classification","summary":"","category":"Terrain - Landform Indices","taxonomy_category":"terrain","taxonomy_subcategory":"landform_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"slope_threshold","description":"Slope Threshold","type":"float","required":false,"default":3,"options":[],"kind":"double"},{"name":"prof_curv_threshold","description":"Prof Curv Threshold","type":"float","required":false,"default":0.1,"options":[],"kind":"double"},{"name":"plan_curv_threshold","description":"Plan Curv Threshold","type":"float","required":false,"default":0,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, str]"},{"id":"percent_elev_range","display_name":"Percent Elev Range","summary":"","category":"Terrain - Landform Indices","taxonomy_category":"terrain","taxonomy_subcategory":"landform_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"relative_topographic_position","display_name":"Relative Topographic Position","summary":"","category":"Terrain - Landform Indices","taxonomy_category":"terrain","taxonomy_subcategory":"landform_indices","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"filter_size_x","description":"Filter Size X","type":"int","required":false,"default":11,"options":[],"kind":"int"},{"name":"filter_size_y","description":"Filter Size Y","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"max_anisotropy_dev","display_name":"Max Anisotropy Dev","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":3,"options":[],"kind":"int"},{"name":"max_scale","description":"Max Scale","type":"int","required":false,"default":100,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_scale","description":"Output Scale","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"max_anisotropy_dev_signature","display_name":"Max Anisotropy Dev Signature","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"max_scale","description":"Max Scale","type":"int","required":false,"default":100,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"max_difference_from_mean","display_name":"Max Difference From Mean","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"max_scale","description":"Max Scale","type":"int","required":false,"default":100,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_scale","description":"Output Scale","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"max_elevation_deviation","display_name":"Max Elevation Deviation","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"max_scale","description":"Max Scale","type":"int","required":false,"default":100,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_scale","description":"Output Scale","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"max_elev_dev_signature","display_name":"Max Elev Dev Signature","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"max_scale","description":"Max Scale","type":"int","required":false,"default":100,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"multiscale_curvatures","display_name":"Multiscale Curvatures","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"curv_type","description":"Curv Type","type":"str","required":false,"default":"profile","options":[],"kind":"string"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":4,"options":[],"kind":"int"},{"name":"step","description":"Step","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"num_steps","description":"Num Steps","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"step_nonlinearity","description":"Step Nonlinearity","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"log_transform","description":"Log Transform","type":"bool","required":false,"default":true,"options":[],"kind":"bool"},{"name":"standardize","description":"Standardize","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"multiscale_elevated_index","display_name":"Multiscale Elevated Index","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"num_steps","description":"Num Steps","type":"int","required":false,"default":100,"options":[],"kind":"int"},{"name":"step_nonlinearity","description":"Step Nonlinearity","type":"float","required":false,"default":1.1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_scale","description":"Output Scale","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"multiscale_elevation_percentile","display_name":"Multiscale Elevation Percentile","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":4,"options":[],"kind":"int"},{"name":"num_steps","description":"Num Steps","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"step_nonlinearity","description":"Step Nonlinearity","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"sig_digits","description":"Sig Digits","type":"int","required":false,"default":3,"options":[],"kind":"int"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_scale","description":"Output Scale","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"multiscale_low_lying_index","display_name":"Multiscale Low Lying Index","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":2,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"num_steps","description":"Num Steps","type":"int","required":false,"default":100,"options":[],"kind":"int"},{"name":"step_nonlinearity","description":"Step Nonlinearity","type":"float","required":false,"default":1.1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_scale","description":"Output Scale","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"multiscale_roughness","display_name":"Multiscale Roughness","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"max_scale","description":"Max Scale","type":"int","required":false,"default":100,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_scale","description":"Output Scale","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"multiscale_roughness_signature","display_name":"Multiscale Roughness Signature","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"max_scale","description":"Max Scale","type":"int","required":false,"default":100,"options":[],"kind":"int"},{"name":"step_size","description":"Step Size","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"multiscale_std_dev_normals","display_name":"Multiscale Std Dev Normals","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"step","description":"Step","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"num_steps","description":"Num Steps","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"step_nonlinearity","description":"Step Nonlinearity","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"},{"name":"output_scale","description":"Output Scale","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"tuple[Raster, Raster]"},{"id":"multiscale_std_dev_normals_signature","display_name":"Multiscale Std Dev Normals Signature","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"points","description":"Points","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"step","description":"Step","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"num_steps","description":"Num Steps","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"step_nonlinearity","description":"Step Nonlinearity","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"str"},{"id":"multiscale_topographic_position_image","display_name":"Multiscale Topographic Position Image","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"local","description":"Local","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"meso","description":"Meso","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"broad","description":"Broad","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"hillshade","description":"Hillshade","type":"Raster | str | None","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"lightness","description":"Lightness","type":"float","required":false,"default":1.2,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"topographic_position_animation","display_name":"Topographic Position Animation","summary":"","category":"Terrain - Multiscale Signatures","taxonomy_category":"terrain","taxonomy_subcategory":"multiscale_signatures","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"palette","description":"Palette","type":"str","required":false,"default":"soft","options":[],"kind":"string"},{"name":"min_scale","description":"Min Scale","type":"int","required":false,"default":1,"options":[],"kind":"int"},{"name":"num_steps","description":"Num Steps","type":"int","required":false,"default":10,"options":[],"kind":"int"},{"name":"step_nonlinearity","description":"Step Nonlinearity","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"image_height","description":"Image Height","type":"int","required":false,"default":600,"options":[],"kind":"int"},{"name":"delay","description":"Delay","type":"int","required":false,"default":250,"options":[],"kind":"int"},{"name":"label","description":"Label","type":"str","required":false,"default":"","options":[],"kind":"string"},{"name":"use_dev_max","description":"Use Dev Max","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"tuple[str, str]"},{"id":"topo_render","display_name":"Topo Render","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"open","locked":false,"locked_reason":null,"params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"palette","description":"Palette","type":"str","required":false,"default":"soft","options":[],"kind":"string"},{"name":"reverse_palette","description":"Reverse Palette","type":"bool","required":false,"default":false,"options":[],"kind":"bool"},{"name":"azimuth","description":"Azimuth","type":"float","required":false,"default":315,"options":[],"kind":"double"},{"name":"altitude","description":"Altitude","type":"float","required":false,"default":30,"options":[],"kind":"double"},{"name":"clipping_polygon","description":"Clipping Polygon","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"background_hgt_offset","description":"Background Hgt Offset","type":"float","required":false,"default":10,"options":[],"kind":"double"},{"name":"background_clr","description":"Background Clr","type":"tuple[int, int, int, int]","required":false,"default":[255,255,255,255],"options":[],"kind":"int"},{"name":"attenuation_parameter","description":"Attenuation Parameter","type":"float","required":false,"default":0.3,"options":[],"kind":"double"},{"name":"ambient_light","description":"Ambient Light","type":"float","required":false,"default":0.2,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"max_dist","description":"Max Dist","type":"float | None","required":false,"default":null,"options":[],"kind":"double"},{"name":"output","description":"Output","type":"str | None","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Raster"},{"id":"wetland_hydrogeomorphic_classification","display_name":"Wetland Hydrogeomorphic Classification","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"dem","description":"Input DEM raster path.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"wetland_mask","description":"Input wetland mask raster (>0 treated as wetland).","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"max_polygon_features","description":"Maximum wetland polygon features to emit.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"wetland_region","description":"Regional HGM calibration pack.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"urban_expansion_impact_assessment","display_name":"Urban Expansion Impact Assessment","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"baseline_urban","description":"Baseline urban extent raster (binary or fractional).","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"scenario_urban","description":"Scenario/future urban extent raster (binary or fractional).","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Input stream network vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"habitat_sensitivity","description":"Optional habitat sensitivity raster (0..1).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"scenario_template","description":"Planning scenario template for policy comparison outputs.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"wind_turbine_siting","display_name":"Wind Turbine Siting","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"settlements","description":"Settlements","type":"Vector | str","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"settlements_epsg","description":"Settlements Epsg","type":"int | None","required":false,"default":null,"options":[],"kind":"int"},{"name":"visibility_radius_meters","description":"Visibility Radius Meters","type":"int","required":false,"default":5000,"options":[],"kind":"int"},{"name":"min_slope_degrees","description":"Min Slope Degrees","type":"float","required":false,"default":5,"options":[],"kind":"double"},{"name":"max_slope_degrees","description":"Max Slope Degrees","type":"float","required":false,"default":35,"options":[],"kind":"double"},{"name":"profile","description":"Profile","type":"str","required":false,"default":"balanced","options":[],"kind":"file_in"},{"name":"sweep_spec_json","description":"Sweep Spec Json","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output_prefix","description":"Output Prefix","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"solar_site_suitability_analysis","display_name":"Solar Site Suitability Analysis","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"dem","description":"Dem","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"transmission_lines","description":"Transmission Lines","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"substations","description":"Substations","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"road_network","description":"Road Network","type":"Vector | str | None","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"infra_weight_profile","description":"Infra Weight Profile","type":"Literal[\"balanced\", \"grid_priority\", \"access_priority\"]","required":false,"default":"balanced","options":[],"kind":"file_in"},{"name":"candidate_threshold","description":"Candidate Threshold","type":"float","required":false,"default":0.7,"options":[],"kind":"double"},{"name":"max_candidate_sites","description":"Max Candidate Sites","type":"int","required":false,"default":200,"options":[],"kind":"int"},{"name":"sweep_spec_json","description":"Sweep Spec Json","type":"str | None","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output_prefix","description":"Output Prefix","type":"str | None","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"corridor_mapping_intelligence","display_name":"Corridor Mapping Intelligence","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"dem","description":"Input DEM raster path.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"start_features","description":"Start feature vector path (point or polygon).","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"end_features","description":"End feature vector path (point or polygon).","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"constraints","description":"Optional exclusion zone vector path (polygons to avoid).","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"cost_profile","description":"Cost weighting: slope_only | slope_roughness | conservative. Default: slope_roughness.","type":"file","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"terminal_anchor_strategy","description":"Anchor selection strategy for polygon terminals: mixed | centroid_only | boundary_only. Default: mixed.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"corridor_tolerance","description":"Fraction above optimal cost for the suitability band (0–1). Default 0.15.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"landslide_susceptibility_assessment","display_name":"Landslide Susceptibility Assessment","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"dem","description":"Input DEM raster path.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"rainfall_intensity","description":"Optional rainfall intensity proxy raster normalized to [0,1].","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"profile","description":"Preset profile controlling susceptibility sensitivity: fast, balanced, or conservative.","type":"file","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"factor_contribution_mode","description":"Factor contribution output detail: none | basic | detailed. Default: basic.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"susceptibility_threshold","description":"Threshold in [0,1] for high-risk fraction reporting.","type":"number","required":false,"default":null,"options":[],"kind":"double"},{"name":"max_zone_features","description":"Maximum number of risk-zone polygon features to emit.","type":"number","required":false,"default":null,"options":[],"kind":"int"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"river_corridor_health_assessment","display_name":"River Corridor Health Assessment","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"dem","description":"Input DEM raster path.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"streams","description":"Input stream network vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"profile","description":"Preset profile controlling erosion sensitivity: fast, balanced, or conservative.","type":"file","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"restoration_priority_mode","description":"Restoration priority ranking mode: health_score_only | cost_weighted | ecological_gradient. Default: health_score_only.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"baseline_matching_and_diagnostics_assessment","display_name":"Baseline Matching And Diagnostics Assessment","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"project_boundary","description":"Project activity boundary polygon vector.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"ecoregion_layer","description":"Level-3 ecoregion polygons for donor-pool filtering.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"year0_landcover","description":"Year-0 land-cover raster used for non-forest donor eligibility.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"pre_period_ndvi_stack","description":"Multi-band pre-period NDVI stack (recommended years -5 to -1).","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"elevation","description":"Elevation raster covariate.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"precipitation","description":"Precipitation raster covariate.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"soil_organic_carbon","description":"Soil organic carbon raster covariate.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"soil_texture_class","description":"Soil texture class raster covariate.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"roads","description":"Roads vector for accessibility covariates.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"tenure_status","description":"Optional tenure/legal-status vector for exact-match constraints.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"donor_pool_exclusion_distance_km","description":"Exclusion distance from project boundary in km.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"matching_mode","description":"Matching mode: mahalanobis or propensity_score.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"neighbors","description":"Nearest-neighbor count.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"with_replacement","description":"Whether matching is with replacement.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"tenure_field","description":"Attribute field in tenure_status layer used for exact legal-status matching.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"calliper_elevation_m","description":"Hard calliper for elevation in absolute metres.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"calliper_slope_deg","description":"Hard calliper for slope in absolute degrees.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"calliper_soc_pct","description":"Hard calliper for SOC as a percentage of the project-area mean.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"calliper_ndvi_pct","description":"Hard calliper for mean pre-period NDVI as a percentage of the project-area mean.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"calliper_road_distance_km","description":"Hard calliper for road distance in absolute kilometres.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"exact_match_soil_texture_class","description":"Require exact donor/treated soil texture class match.","type":"lidar","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"random_seed","description":"Deterministic seed used for bounded preview sampling.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"smd_threshold","description":"SMD pass threshold (default 0.1).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"parallel_trend_tolerance","description":"Absolute treated-donor slope tolerance for pre-period trends (default 0.01).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"pre_period_min_years","description":"Minimum NDVI pre-period band count.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"carbon_sequestration_verification_audit","display_name":"Carbon Sequestration Verification Audit","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"baseline_bundle","description":"Baseline date multiband raster.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"baseline_red_band_index","description":"Band index (0-based) for red band in baseline_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"baseline_nir_band_index","description":"Band index (0-based) for NIR band in baseline_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"current_bundle","description":"Current date multiband raster.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"current_red_band_index","description":"Band index (0-based) for red band in current_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"current_nir_band_index","description":"Band index (0-based) for NIR band in current_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"biomass_proxy","description":"Optional LiDAR-derived biomass proxy raster.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"biome_class","description":"Biome for carbon coefficient tuning: tropical_forest, boreal_forest, grassland_shrub, agricultural_cropland, wetland_aquatic, none.","type":"lidar","required":false,"default":null,"options":[],"kind":"lidar_in"},{"name":"profile","description":"Detection sensitivity profile: conservative, balanced, aggressive.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"zone_block_cells","description":"Block aggregation size for verification zone polygons.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"baseline_date","description":"Optional baseline capture date (YYYY-MM-DD) for temporal uncertainty estimation.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"current_date","description":"Optional current capture date (YYYY-MM-DD) for temporal uncertainty estimation.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"mrv_template","description":"MRV compliance framework: verra_vcs_vm0010, american_carbon_registry, gold_standard, none.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"methodology_reference","description":"Optional methodology reference string for the audit contract.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"baseline_matching_audit_contract","description":"Optional SEMDB summary contract JSON for integrated diagnostics handoff.","type":"json","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"wildfire_fuel_loading_and_risk_matrix","display_name":"Wildfire Fuel Loading And Risk Matrix","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"optical_bundle","description":"Multispectral raster with red and NIR (and optionally SWIR) bands.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"red_band_index","description":"Band index (0-based) for red band.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"nir_band_index","description":"Band index (0-based) for NIR band.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"swir_band_index","description":"Band index (0-based) for SWIR band (optional; enables NDMI).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"biomass_proxy","description":"Optional LiDAR-derived canopy height or biomass proxy raster.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"slope","description":"Optional terrain slope raster (degrees).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"aspect","description":"Optional terrain aspect raster (degrees).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"fuel_model","description":"Fuel model for regional calibration: temperate_mixed_forest, boreal_sparse, mediterranean_shrub, tropical_dense, grassland_savanna, none.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"profile","description":"Risk sensitivity profile: conservative, balanced, aggressive.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"zone_block_cells","description":"Block aggregation size for risk zone polygons.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"mine_site_reclamation_compliance_tracker","display_name":"Mine Site Reclamation Compliance Tracker","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"baseline_bundle","description":"Pre-mining or early reclamation date multiband raster.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"baseline_red_band_index","description":"Band index (0-based) for red band in baseline_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"baseline_nir_band_index","description":"Band index (0-based) for NIR band in baseline_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"current_bundle","description":"Current monitoring date multiband raster.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"current_red_band_index","description":"Band index (0-based) for red band in current_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"current_nir_band_index","description":"Band index (0-based) for NIR band in current_bundle.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"slope","description":"Optional terrain slope raster (degrees).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"reclamation_target_ndvi","description":"Target NDVI threshold for vegetation recovery milestone.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"slope_stability_max_deg","description":"Maximum acceptable slope angle for stability milestone (degrees).","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"jurisdiction","description":"Jurisdiction-specific compliance template: us_federal_mtbs, us_california_mining, us_pennsylvania_coal, aus_western_australia, canada_bc_mines, south_africa_dmre, none.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"monitoring_epoch","description":"Monitoring epoch for multi-epoch tracking.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"site_name","description":"Optional site name or permit ID.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"has_hydrology_evidence","description":"Whether supporting hydraulic stability evidence is attached.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"has_soil_ph_evidence","description":"Whether soil pH evidence is attached when required.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"has_perennial_vegetation_evidence","description":"Whether perennial vegetation establishment evidence is attached when required.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"report_interval_months","description":"Reported monitoring cadence in months for validation diagnostics.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"zone_block_cells","description":"Block size in cells for compliance zone polygons.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"terrain_constraint_and_conflict_analysis","display_name":"Terrain Constraint And Conflict Analysis","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"dem","description":"Reference DEM raster path.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"wetness","description":"Optional wetness or moisture-risk raster normalized to [0,1].","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"flood_risk","description":"Optional flood-risk raster normalized to [0,1].","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"landcover_penalty","description":"Optional landcover penalty raster normalized to [0,1].","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"slope_limit_deg","description":"Slope threshold at which terrain conflict becomes high.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for conflict rasters and summary.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"terrain_constructability_and_cost_analysis","display_name":"Terrain Constructability And Cost Analysis","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"dem","description":"Reference DEM raster path.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"existing_conflict","description":"Optional terrain conflict raster from a prior workflow normalized to [0,1].","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"wetness","description":"Optional wetness raster normalized to [0,1].","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"access_cost","description":"Optional access-friction raster normalized to [0,1].","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output_prefix","description":"Output prefix for constructability/cost rasters and summary.","type":"raster","required":false,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"utility_corridor_encroachment_intelligence","display_name":"Utility Corridor Encroachment Intelligence","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"input","description":"Input LiDAR paths (single path, delimited string, or array).","type":"lidar","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"corridors","description":"Corridor centerlines vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"corridor_type","description":"Corridor type profile.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"corridor_width_m","description":"Total corridor width in meters.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"ground_percentile","description":"Ground candidate percentile in [0,1].","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"k_neighbors","description":"K nearest ground points for HAG.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"hag_reuse_cell_m","description":"Optional XY cell size for HAG query reuse/downsampling.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"chainage_window_m","description":"Risk table chainage window size in meters.","type":"table","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"summary_json","description":"Output summary JSON path.","type":"json","required":false,"default":null,"options":[],"kind":"file_in"},{"name":"events_output","description":"Optional output GeoPackage path for encroachment event features.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"intervals_output","description":"Optional output GeoPackage path for merged interval features.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"windows_output","description":"Optional output GeoPackage path for risk window features.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"}],"return_type":"Any"},{"id":"forestry_structure_and_biomass_intelligence","display_name":"Forestry Structure And Biomass Intelligence","summary":"","category":"Terrain - Workflow Products","taxonomy_category":"terrain","taxonomy_subcategory":"workflow_products","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"input","description":"Input LiDAR path.","type":"lidar","required":true,"default":null,"options":[],"kind":"lidar_in"},{"name":"profile","description":"Profile preset: fast, balanced, strict.","type":"file","required":true,"default":null,"options":[],"kind":"file_in"},{"name":"resolution","description":"Output raster resolution.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"stand_block_cells","description":"Stand aggregation block size in cells.","type":"number","required":true,"default":null,"options":[],"kind":"int"},{"name":"biomass_cap","description":"Maximum biomass proxy value.","type":"number","required":true,"default":null,"options":[],"kind":"double"},{"name":"terrain_adaptation","description":"Biomass terrain adaptation mode.","type":"string","required":true,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"},{"id":"field_trafficability_and_operation_planning","display_name":"Field Trafficability And Operation Planning","summary":"","category":"Precision Agriculture","taxonomy_category":"precision_agriculture","taxonomy_subcategory":"general","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"dem","description":"Reference DEM raster path.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"soil_moisture","description":"Soil-moisture saturation raster normalized to [0,1].","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"rainfall_forecast","description":"Optional rainfall-risk raster normalized to [0,1].","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output_prefix","description":"Output prefix for trafficability outputs and summary.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"in_season_crop_stress_intervention_planning","display_name":"In Season Crop Stress Intervention Planning","summary":"","category":"Precision Agriculture","taxonomy_category":"precision_agriculture","taxonomy_subcategory":"general","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"ndvi","description":"Input NDVI or vigor raster normalized to [0,1].","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"canopy_temperature","description":"Optional canopy-temperature stress raster normalized to [0,1].","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"soil_moisture","description":"Optional soil-moisture deficit raster normalized to [0,1].","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"output_prefix","description":"Output prefix for intervention rasters and summary.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_out"}],"return_type":"Any"},{"id":"precision_irrigation_optimization","display_name":"Precision Irrigation Optimization","summary":"","category":"Precision Agriculture","taxonomy_category":"precision_agriculture","taxonomy_subcategory":"general","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"dem","description":"Input DEM raster path.","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"soil_moisture","description":"Optional soil moisture raster normalized to [0,1].","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"profile","description":"Preset profile controlling irrigation aggressiveness: fast, balanced, or conservative.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"target_moisture","description":"Target moisture setpoint in [0,1].","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_irrigation_mm","description":"Maximum irrigation depth in mm.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"sweep_spec","description":"Optional parameter sweep specification object (experimental foundation for scenario sweeps).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"precision_ag_yield_zone_intelligence","display_name":"Precision Ag Yield Zone Intelligence","summary":"","category":"Precision Agriculture","taxonomy_category":"precision_agriculture","taxonomy_subcategory":"general","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"yield_surface","description":"Input yield raster (relative yield or normalized productivity).","type":"raster","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"terrain_context","description":"Optional terrain context raster (e.g., wetness or slope proxy).","type":"raster","required":false,"default":null,"options":[],"kind":"raster_in"},{"name":"profile","description":"Preset profile controlling zoning sensitivity: fast, balanced, or conservative.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"zone_count","description":"Number of management zones.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_zone_features","description":"Maximum number of management-zone polygon features to emit.","type":"vector","required":false,"default":null,"options":[],"kind":"vector_in"},{"name":"sweep_spec","description":"Optional parameter sweep specification object (experimental foundation for scenario sweeps).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"output_prefix","description":"Output prefix for generated artifacts.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"}],"return_type":"Any"},{"id":"soil_landscape_classification","display_name":"Soil Landscape Classification","summary":"","category":"Precision Agriculture","taxonomy_category":"precision_agriculture","taxonomy_subcategory":"general","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"input","description":"Input","type":"Raster | str","required":true,"default":null,"options":[],"kind":"raster_in"},{"name":"flat_slope_threshold","description":"Flat Slope Threshold","type":"float","required":false,"default":3,"options":[],"kind":"double"},{"name":"profile_curvature_threshold","description":"Profile Curvature Threshold","type":"float","required":false,"default":0.01,"options":[],"kind":"double"},{"name":"plan_curvature_threshold","description":"Plan Curvature Threshold","type":"float","required":false,"default":0.01,"options":[],"kind":"double"},{"name":"fine_scale","description":"Fine Scale","type":"float","required":false,"default":2,"options":[],"kind":"double"},{"name":"coarse_scale","description":"Coarse Scale","type":"float","required":false,"default":8,"options":[],"kind":"double"},{"name":"z_factor","description":"Z Factor","type":"float","required":false,"default":1,"options":[],"kind":"double"},{"name":"output_prefix","description":"Output Prefix","type":"str | None","required":true,"default":null,"options":[],"kind":"vector_out"},{"name":"landform_polygons_output","description":"Landform Polygons Output","type":"str | None","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"tuple[Raster, Raster, Vector, str]"},{"id":"yield_data_conditioning_and_qa","display_name":"Yield Data Conditioning And Qa","summary":"","category":"Precision Agriculture","taxonomy_category":"precision_agriculture","taxonomy_subcategory":"general","license_tier":"pro","locked":true,"locked_reason":"Pro license required","params":[{"name":"input","description":"Input yield point vector path.","type":"vector","required":true,"default":null,"options":[],"kind":"vector_in"},{"name":"output_prefix","description":"Output prefix for all generated artifacts.","type":"file","required":true,"default":null,"options":[],"kind":"file_out"},{"name":"keep_intermediates","description":"If true, preserve intermediate workflow artifacts (default true).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"intermediate_dir","description":"Optional directory for intermediate artifacts.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"yield_field_name","description":"Raw yield field name (default YIELD).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"use_field_aliases","description":"If true, auto-resolve common field aliases when configured fields are missing (default true).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"moisture_field_name","description":"Optional grain moisture field name for dry-yield normalization (e.g., MOISTURE).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"target_moisture_pct","description":"Target moisture percent for dry-yield normalization when moisture_field_name is supplied.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"speed_field_name","description":"Optional speed field (km/h) for telemetry QA filtering.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"heading_field_name","description":"Optional heading field (degrees) for telemetry QA filtering.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"min_speed_kmh","description":"Minimum accepted speed in km/h when speed_field_name is supplied.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_speed_kmh","description":"Maximum accepted speed in km/h when speed_field_name is supplied.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_heading_change_deg","description":"Maximum allowed local heading jump in degrees when heading_field_name is supplied.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"header_field_name","description":"Header/region field name used for reconciliation (default HEADER).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"profile","description":"Pipeline profile: fast, balanced, strict.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"swath_width","description":"Swath width for pass-aware tools (default 6.096).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"edge_radius","description":"Neighborhood radius for edge detection (profile default).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"reconcile_radius","description":"Neighborhood radius for multi-header reconciliation (profile default).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"normalization_radius","description":"Neighborhood radius for local normalization (default depends on profile).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"lag_correction_mode","description":"Optional lag correction mode for pass points: none or distance.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"lag_distance_m","description":"Distance lag in map units for lag_correction_mode=distance.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"filtering_mode","description":"Filtering mode after pass-aware smoothing: standard or robust.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"robust_mad_threshold","description":"MAD threshold for robust filtering mode (default depends on profile).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"z_score_threshold","description":"Outlier z-score threshold for yield filtering (profile default).","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"min_yield","description":"Minimum accepted yield value.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_yield","description":"Maximum accepted yield value.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"mean_tonnage","description":"Optional target mean yield after reconciliation.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"standardize","description":"If true, output standardized values instead of [0,1] normalization.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"ignore_zeros","description":"Ignore zero-yield points during pass reconstruction.","type":"string","required":false,"default":null,"options":[],"kind":"string"},{"name":"max_change_in_heading","description":"Maximum heading change in degrees for pass logic (default 25).","type":"string","required":false,"default":null,"options":[],"kind":"string"}],"return_type":"Any"}]} diff --git a/apps/geolibre-desktop/src-tauri/Cargo.lock b/apps/geolibre-desktop/src-tauri/Cargo.lock new file mode 100644 index 0000000..29edc36 --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/Cargo.lock @@ -0,0 +1,6601 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "const-random", + "getrandom 0.3.4", + "once_cell", + "version_check", + "zerocopy", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arrayvec" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02882884d3e1bc524fb12c79f107f6ad0e1cfd498c536ffb494301740995dfe" + +[[package]] +name = "arrow" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "378530e55cd479eda3c14eb345310799717e6f76d0c332041e8487022166b471" +dependencies = [ + "arrow-arith", + "arrow-array", + "arrow-buffer", + "arrow-cast", + "arrow-data", + "arrow-ord", + "arrow-row", + "arrow-schema", + "arrow-select", + "arrow-string", +] + +[[package]] +name = "arrow-arith" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0ab212d2c1886e802f51c5212d78ebbcbb0bec980fff9dadc1eb8d45cd0b738" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "chrono", + "num-traits", +] + +[[package]] +name = "arrow-array" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfd33d3e92f207444098c75b42de99d329562be0cf686b307b097cc52b4e999e" +dependencies = [ + "ahash 0.8.12", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "chrono", + "half", + "hashbrown 0.17.1", + "num-complex", + "num-integer", + "num-traits", +] + +[[package]] +name = "arrow-buffer" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6cd424c2693bcdbc150d843dc9d4d137dd2de4782ce6df491ad11a3a0416c0" +dependencies = [ + "bytes", + "half", + "num-bigint", + "num-traits", +] + +[[package]] +name = "arrow-cast" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c5aefb56a2c02e9e2b30746241058b85f8983f0fcff2ba0c6d09006e1cded7f" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-ord", + "arrow-schema", + "arrow-select", + "atoi", + "base64 0.22.1", + "chrono", + "comfy-table", + "half", + "lexical-core", + "num-traits", + "ryu", +] + +[[package]] +name = "arrow-data" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c88210023a2bfee1896af366309a3028fc3bcbd6515fa29a7990ee1baa08ee0" +dependencies = [ + "arrow-buffer", + "arrow-schema", + "half", + "num-integer", + "num-traits", +] + +[[package]] +name = "arrow-ord" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bffd8fd2579286a5d63bac898159873e5094a79009940bcb42bbfce4f19f1d0" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", +] + +[[package]] +name = "arrow-row" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bab5994731204603c73ba69267616c50f80780774c6bb0476f1f830625115e0c" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "half", +] + +[[package]] +name = "arrow-schema" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f633dbfdf39c039ada1bf9e34c694816eb71fbb7dc78f613993b7245e078a1ed" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "arrow-select" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd065c54172ac787cf3f2f8d4107e0d3fdc26edba76fdf4f4cc170258942222" +dependencies = [ + "ahash 0.8.12", + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "num-traits", +] + +[[package]] +name = "arrow-string" +version = "58.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29dd7cda3ab9692f43a2e4acc444d760cc17b12bb6d8232ddf64e9bab7c06b42" +dependencies = [ + "arrow-array", + "arrow-buffer", + "arrow-data", + "arrow-schema", + "arrow-select", + "memchr", + "num-traits", + "regex", + "regex-syntax", +] + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.1.4", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.1.4", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.4", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" +dependencies = [ + "serde_core", +] + +[[package]] +name = "bitvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "borsh" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145" +dependencies = [ + "borsh-derive", + "bytes", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8fb4fb5740e4b2c4884ff95f5f32f5e8479db1e8fd8eb49ddbe09eb09bb7c" +dependencies = [ + "once_cell", + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "brotli" +version = "8.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bd8b9603c7aa97359dbd97ecf258968c95f3adddd6db2f7e7a5bef101c84560" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.11.1", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "cargo_toml" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +dependencies = [ + "serde", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link 0.2.1", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "comfy-table" +version = "7.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a" +dependencies = [ + "crossterm", + "unicode-segmentation", + "unicode-width", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.10.1", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.10.1", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags 2.11.1", + "crossterm_winapi", + "parking_lot", + "rustix 0.38.44", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "ctor" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.117", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + +[[package]] +name = "dbus" +version = "0.9.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b942602992bb7acfd1f51c49811c58a610ef9181b6e66f3e519d79b540a3bf73" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", + "serde_core", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.117", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.11.1", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "dom_query" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +dependencies = [ + "bit-set", + "cssparser", + "foldhash 0.2.0", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dtor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "duckdb" +version = "1.10504.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9b997383221efd999a362448a0866c54b9a2cb7d4da8cd4903ead4df9f8eaa" +dependencies = [ + "arrow", + "cast", + "comfy-table", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink 0.10.0", + "libduckdb-sys", + "num-integer", + "rust_decimal", + "strum", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "embed-resource" +version = "3.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31a88c8d26de40ed18fe748c547845aa39de1db3afd958f8cb91579f3644bcb" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 1.1.2+spec-1.1.0", + "vswhom", + "winreg", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fallible-iterator" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "geolibre-desktop" +version = "2.0.0" +dependencies = [ + "chrono", + "duckdb", + "flate2", + "getrandom 0.4.2", + "reqwest 0.12.28", + "rusqlite", + "serde", + "serde_json", + "tar", + "tauri", + "tauri-build", + "tauri-plugin-dialog", + "tauri-plugin-fs", + "tauri-plugin-http", + "tauri-plugin-opener", + "tauri-plugin-persisted-scope", + "webkit2gtk-sys", + "zip", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.11.1", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +dependencies = [ + "foldhash 0.2.0", +] + +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + +[[package]] +name = "hashlink" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32069d97bb81e38fa67eab65e3393bf804bb85969f2bc06bf13f64aef5aba248" +dependencies = [ + "hashbrown 0.17.1", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "html5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +dependencies = [ + "log", + "markup5ever", +] + +[[package]] +name = "http" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb92f162bf56536459fc83c79b974bb12837acfed43d6bc370a7916d0ae15ecc" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" +dependencies = [ + "byteorder", + "png 0.17.16", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.11.1", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "lexical-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8d125a277f807e55a77304455eb7b1cb52f2b18c143b60e766c120bd64a594" +dependencies = [ + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a9f232fbd6f550bc0137dcb5f99ab674071ac2d690ac69704593cb4abbea56" +dependencies = [ + "lexical-parse-integer", + "lexical-util", +] + +[[package]] +name = "lexical-parse-integer" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a7a039f8fb9c19c996cd7b2fcce303c1b2874fe1aca544edc85c4a5f8489b34" +dependencies = [ + "lexical-util", +] + +[[package]] +name = "lexical-util" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2604dd126bb14f13fb5d1bd6a66155079cb9fa655b37f875b3a742c705dbed17" + +[[package]] +name = "lexical-write-float" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50c438c87c013188d415fbabbb1dceb44249ab81664efbd31b14ae55dabb6361" +dependencies = [ + "lexical-util", + "lexical-write-integer", +] + +[[package]] +name = "lexical-write-integer" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "409851a618475d2d5796377cad353802345cba92c867d9fbcde9cf4eac4e14df" +dependencies = [ + "lexical-util", +] + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libduckdb-sys" +version = "1.10504.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e67523a40ee3da30411e00e243990b3760d91877202ab2f39c03ab34f8dbfc" +dependencies = [ + "cc", + "flate2", + "pkg-config", + "reqwest 0.12.28", + "serde", + "serde_json", + "tar", + "vcpkg", + "zip", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libredox" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3" +dependencies = [ + "libc", +] + +[[package]] +name = "libsqlite3-sys" +version = "0.38.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6c19a05435c21ac299d71b6a9c13db3e3f47c520517d58990a462a1397a61db" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "markup5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "muda" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47a2e3dff89cd322c66647942668faee0a2b1f88ea6cbb4d374b4a8d7e92528c" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.11.1", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.11.1", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.11.1", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.11.1", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.11.1", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.11.1", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.11.1", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.11.1", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +dependencies = [ + "bitflags 2.11.1", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "open" +version = "5.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fbaa89d2ddc8473c78a3adf69eea8cffa28c483b8e02a971ef31527cd0fc92c" +dependencies = [ + "dunce", + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plist" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" +dependencies = [ + "base64 0.22.1", + "indexmap 2.14.0", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.11.1", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.12+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + +[[package]] +name = "quick-xml" +version = "0.39.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.11.1", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "cookie", + "cookie_store", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "block2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rsqlite-vfs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" +dependencies = [ + "hashbrown 0.16.1", + "thiserror 2.0.18", +] + +[[package]] +name = "rusqlite" +version = "0.40.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11438310b19e3109b6446c33d1ed5e889428cf2e278407bc7896bc4aaea43323" +dependencies = [ + "bitflags 2.11.1", + "fallible-iterator", + "fallible-streaming-iterator", + "hashlink 0.12.1", + "libsqlite3-sys", + "smallvec", + "sqlite-wasm-rs", +] + +[[package]] +name = "rust_decimal" +version = "1.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2a24f50780bc85f09cc6ac299bdf1424302742d77221106859c9d8b102126a" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.6", + "rkyv", + "serde", + "serde_json", + "wasm-bindgen", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.11.1", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.117", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "selectors" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" +dependencies = [ + "bitflags 2.11.1", + "cssparser", + "derive_more", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "rustc-hash", + "servo_arc", + "smallvec", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" +dependencies = [ + "base64 0.22.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "softbuffer" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +dependencies = [ + "bytemuck", + "js-sys", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall", + "tracing", + "wasm-bindgen", + "web-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "sqlite-wasm-rs" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc3efc0da82635d7e1ced0053bbbfa8c7ab9645d0bf36ceb4f7127bb85315d75" +dependencies = [ + "cc", + "js-sys", + "rsqlite-vfs", + "wasm-bindgen", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.35.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" +dependencies = [ + "bitflags 2.11.1", + "block2", + "core-foundation 0.10.1", + "core-graphics", + "crossbeam-channel", + "dbus", + "dispatch2", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni", + "libc", + "log", + "ndk", + "ndk-sys", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "once_cell", + "parking_lot", + "percent-encoding", + "raw-window-handle", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "437404997acf375d85f1177afa7e11bb971f274ed6a7b83a2a3e339015f4cc28" +dependencies = [ + "anyhow", + "bytes", + "cookie", + "dirs", + "dunce", + "embed_plist", + "getrandom 0.3.4", + "glob", + "gtk", + "heck 0.5.0", + "http", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest 0.13.4", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.18", + "tokio", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-build" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4a0319528a025a38c4078e7dae2c446f4e63620ddb0659a643ede1cb38f90e9" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png 0.17.16", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.117", + "tauri-utils", + "thiserror 2.0.18", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae6cb4e3896c21d2f6da5b31251d2faea0153bba56ed0e970f918115dbee4924" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e126abc9e84e35cdfd01596140a73a1850cdb0df0a23acf0185776c30b469a6e" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri-utils", + "walkdir", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65981abb771e74e571a38196c3baa11c459379164791eba0e67abc1a5fac9884" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ecc274121aca0c036a2b42d1cbe83d368d348f54e0bb8a735c2b1548e8f371" +dependencies = [ + "anyhow", + "dunce", + "glob", + "log", + "objc2-foundation", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", + "url", +] + +[[package]] +name = "tauri-plugin-http" +version = "2.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5bd512048e1985b7ec78f96d99083e2ddaf7e0d906b2b63c44ce5bb8b894067" +dependencies = [ + "bytes", + "cookie_store", + "data-url", + "http", + "regex", + "reqwest 0.12.28", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "tokio", + "url", + "urlpattern", +] + +[[package]] +name = "tauri-plugin-opener" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17e1bea14edce6b793a04e2417e3fd924b9bc4faae83cdee7d714156cceeed29" +dependencies = [ + "dunce", + "glob", + "objc2-app-kit", + "objc2-foundation", + "open", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", + "url", + "windows", + "zbus", +] + +[[package]] +name = "tauri-plugin-persisted-scope" +version = "2.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b560a5962bf975d38fb4ec98a0e64e52929992ec708acb812add9c1ab8d186d" +dependencies = [ + "aho-corasick", + "bincode", + "log", + "serde", + "serde_json", + "tauri", + "tauri-plugin-fs", + "thiserror 2.0.18", +] + +[[package]] +name = "tauri-runtime" +version = "2.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48222d7116c8807eaa6fe2f372e023fae125084e61e6eca6d70b7961cdf129ef" +dependencies = [ + "cookie", + "dpi", + "gtk", + "http", + "jni", + "objc2", + "objc2-ui-kit", + "objc2-web-kit", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webview2-com", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b83849ee63ecb27a8e8d0fe51915ca215076914aca43f96db1179f0f415f6cd9" +dependencies = [ + "gtk", + "http", + "jni", + "log", + "objc2", + "objc2-app-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887" +dependencies = [ + "anyhow", + "brotli", + "cargo_metadata", + "ctor", + "dom_query", + "dunce", + "glob", + "http", + "infer", + "json-patch", + "log", + "memchr", + "phf", + "plist", + "proc-macro2", + "quote", + "regex", + "schemars 0.8.22", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" +dependencies = [ + "dunce", + "embed-resource", + "toml 1.1.2+spec-1.1.0", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "tendril" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" +dependencies = [ + "new_debug_unreachable", + "utf-8", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.3", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.11.1", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tray-icon" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys 0.61.2", +] + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +dependencies = [ + "getrandom 0.4.2", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "serde", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap 2.14.0", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.11.1", + "hashbrown 0.15.5", + "indexmap 2.14.0", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web_atoms" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core 0.61.2", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "webview2-com-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +dependencies = [ + "thiserror 2.0.18", + "windows", + "windows-core 0.61.2", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap 2.14.0", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.11.1", + "indexmap 2.14.0", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap 2.14.0", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "wry" +version = "0.55.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" +dependencies = [ + "base64 0.22.1", + "block2", + "cookie", + "crossbeam-channel", + "dirs", + "dom_query", + "dpi", + "dunce", + "gdkx11", + "gtk", + "http", + "javascriptcore-rs", + "jni", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2", + "soup3", + "tao-macros", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix 1.1.4", +] + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zbus" +version = "5.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix 1.1.4", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys 0.61.2", + "winnow 1.0.3", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" +dependencies = [ + "serde", + "winnow 1.0.3", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zip" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb2a05c7c36fde6c09b08576c9f7fb4cda705990f73b58fe011abf7dfb24168b" +dependencies = [ + "arbitrary", + "crc32fast", + "flate2", + "indexmap 2.14.0", + "memchr", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "977347db8caa080403f6b6b7c1cda9479a8e869316f7e13a59b19076a40f94e3" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + +[[package]] +name = "zvariant" +version = "5.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow 1.0.3", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.117", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.117", + "winnow 1.0.3", +] diff --git a/apps/geolibre-desktop/src-tauri/Cargo.toml b/apps/geolibre-desktop/src-tauri/Cargo.toml new file mode 100644 index 0000000..7699ab8 --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "geolibre-desktop" +version = "2.0.0" +description = "GeoLibre Desktop — lightweight cloud-native desktop GIS" +authors = ["GeoLibre Contributors"] +edition = "2021" + +[lib] +name = "geolibre_desktop_lib" +crate-type = ["lib", "cdylib", "staticlib"] + +[features] +default = [] +native-duckdb = ["dep:duckdb"] + +# Optimize the release binary for size. The frontend assets dominate the binary, +# but these still trim the Rust code: LTO + a single codegen unit let the linker +# drop dead code across crates, `strip` removes symbols/debuginfo, `opt-level="s"` +# favors size while keeping reasonable runtime speed, and `panic="abort"` drops +# unwinding tables (a panic in the desktop shell terminates the app either way). +[profile.release] +opt-level = "s" +lto = true +codegen-units = 1 +strip = true +panic = "abort" + +[build-dependencies] +tauri-build = { version = "2", features = [] } + +[dependencies] +tauri = { version = "2", features = [] } +tauri-plugin-dialog = "2" +tauri-plugin-fs = "2" +tauri-plugin-http = "2" +tauri-plugin-opener = "2" +# Persists the fs scope the folder picker grants, so Browser-panel pinned +# folders stay readable across app restarts (no re-pick required). +tauri-plugin-persisted-scope = "2" +duckdb = { version = "1.10504.0", features = ["bundled", "parquet"], optional = true } +chrono = { version = "0.4", default-features = false, features = ["std"] } +reqwest = { version = "0.12", default-features = false, features = ["blocking", "rustls-tls"] } +flate2 = "1" +tar = "0.4" +zip = { version = "6", default-features = false, features = ["deflate"] } +rusqlite = { version = "0.40", features = ["bundled"] } +serde = { version = "1", features = ["derive"] } +serde_json = "1" +# OS CSPRNG for the per-launch Jupyter token (already an indirect dep). +getrandom = "0.4" + +# Runtime WebKitGTK version check (already an indirect dep via tauri/wry). +[target."cfg(target_os = \"linux\")".dependencies] +webkit2gtk-sys = "2" diff --git a/apps/geolibre-desktop/src-tauri/build.rs b/apps/geolibre-desktop/src-tauri/build.rs new file mode 100644 index 0000000..d860e1e --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/apps/geolibre-desktop/src-tauri/capabilities/default.json b/apps/geolibre-desktop/src-tauri/capabilities/default.json new file mode 100644 index 0000000..e924fd6 --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/capabilities/default.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "identifier": "default", + "description": "Default capabilities for GeoLibre Desktop", + "windows": ["main"], + "permissions": [ + "core:default", + "dialog:default", + "fs:default", + "fs:allow-read-dir", + "fs:allow-read-file", + "fs:allow-read-text-file", + "fs:allow-write-file", + "fs:allow-write-text-file", + { + "identifier": "opener:allow-open-url", + "allow": [ + { "url": "http://*" }, + { "url": "https://*" } + ] + }, + { + "comment": "Hosts routed through the native HTTP client to bypass the WebView's CORS. Geocoding (place search / reverse geocode): public Nominatim's CDN intermittently omits the CORS header on cached responses (rejected as 'Search failed'), and the native client can send the User-Agent Nominatim's policy requires — keep the geocoder hosts in sync with the GEOCODING_PROVIDERS registry / lib/geocoding-fetch.ts. share.geolibre.app (project Share + gallery): its CORS policy allows the web origin but not the Tauri WebView origin, so browser fetches fail as 'Could not reach share.geolibre.app' — see lib/share-fetch.ts. Any other host (custom/self-hosted geocoder, third-party project URL) falls back to the browser fetch.", + "identifier": "http:default", + "allow": [ + { "url": "https://nominatim.openstreetmap.org/*" }, + { "url": "https://api.geocode.earth/*" }, + { "url": "https://geocode.arcgis.com/*" }, + { "url": "https://api.mapbox.com/*" }, + { "url": "https://maps.googleapis.com/*" }, + { "url": "https://share.geolibre.app/*" } + ] + } + ] +} diff --git a/apps/geolibre-desktop/src-tauri/icons/128x128.png b/apps/geolibre-desktop/src-tauri/icons/128x128.png new file mode 100644 index 0000000..1ffce53 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/128x128.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/128x128@2x.png b/apps/geolibre-desktop/src-tauri/icons/128x128@2x.png new file mode 100644 index 0000000..e2958e9 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/128x128@2x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/32x32.png b/apps/geolibre-desktop/src-tauri/icons/32x32.png new file mode 100644 index 0000000..c1153ed Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/32x32.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/64x64.png b/apps/geolibre-desktop/src-tauri/icons/64x64.png new file mode 100644 index 0000000..960abb3 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/64x64.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/Square107x107Logo.png b/apps/geolibre-desktop/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000..9578baf Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/Square107x107Logo.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/Square142x142Logo.png b/apps/geolibre-desktop/src-tauri/icons/Square142x142Logo.png new file mode 100644 index 0000000..fbca6cb Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/Square142x142Logo.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/Square150x150Logo.png b/apps/geolibre-desktop/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000..3d10b77 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/Square150x150Logo.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/Square284x284Logo.png b/apps/geolibre-desktop/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000..facd0d0 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/Square284x284Logo.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/Square30x30Logo.png b/apps/geolibre-desktop/src-tauri/icons/Square30x30Logo.png new file mode 100644 index 0000000..c8621f2 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/Square30x30Logo.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/Square310x310Logo.png b/apps/geolibre-desktop/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000..2a93464 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/Square310x310Logo.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/Square44x44Logo.png b/apps/geolibre-desktop/src-tauri/icons/Square44x44Logo.png new file mode 100644 index 0000000..4276ba2 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/Square44x44Logo.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/Square71x71Logo.png b/apps/geolibre-desktop/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000..f83d3b3 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/Square71x71Logo.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/Square89x89Logo.png b/apps/geolibre-desktop/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000..7ee4515 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/Square89x89Logo.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/StoreLogo.png b/apps/geolibre-desktop/src-tauri/icons/StoreLogo.png new file mode 100644 index 0000000..dfda1b1 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/StoreLogo.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..03f0660 --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..7857322 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..0f7427c Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..742a45f Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..0a305a9 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..ecdf1de Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..1535974 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..552759d Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..f6ed4e6 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..c5eda5f Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..e297b08 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..b85d56b Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..a9254b9 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..48e94f5 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..f6d011f Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..dbc1d48 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/android/values/ic_launcher_background.xml b/apps/geolibre-desktop/src-tauri/icons/android/values/ic_launcher_background.xml new file mode 100644 index 0000000..3024847 --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/icons/android/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #fff + diff --git a/apps/geolibre-desktop/src-tauri/icons/icon.icns b/apps/geolibre-desktop/src-tauri/icons/icon.icns new file mode 100644 index 0000000..2ca3b2e Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/icon.icns differ diff --git a/apps/geolibre-desktop/src-tauri/icons/icon.ico b/apps/geolibre-desktop/src-tauri/icons/icon.ico new file mode 100644 index 0000000..d87b600 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/icon.ico differ diff --git a/apps/geolibre-desktop/src-tauri/icons/icon.png b/apps/geolibre-desktop/src-tauri/icons/icon.png new file mode 100644 index 0000000..b53671c Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/icon.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@1x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@1x.png new file mode 100644 index 0000000..b230db1 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@1x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000..db90003 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@2x-1.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@2x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@2x.png new file mode 100644 index 0000000..db90003 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@2x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@3x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@3x.png new file mode 100644 index 0000000..e077fe6 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-20x20@3x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@1x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@1x.png new file mode 100644 index 0000000..3d03d81 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@1x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000..a8ac28c Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@2x-1.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@2x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@2x.png new file mode 100644 index 0000000..a8ac28c Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@2x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@3x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@3x.png new file mode 100644 index 0000000..b6a4d40 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-29x29@3x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@1x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@1x.png new file mode 100644 index 0000000..db90003 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@1x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000..588f1c3 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@2x-1.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@2x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@2x.png new file mode 100644 index 0000000..588f1c3 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@2x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@3x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@3x.png new file mode 100644 index 0000000..f9bdcf0 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-40x40@3x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-512@2x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-512@2x.png new file mode 100644 index 0000000..0d039c8 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-512@2x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-60x60@2x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-60x60@2x.png new file mode 100644 index 0000000..f9bdcf0 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-60x60@2x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-60x60@3x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-60x60@3x.png new file mode 100644 index 0000000..3cf7e5e Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-60x60@3x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-76x76@1x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-76x76@1x.png new file mode 100644 index 0000000..45ee8fd Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-76x76@1x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-76x76@2x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-76x76@2x.png new file mode 100644 index 0000000..e9f5656 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-76x76@2x.png differ diff --git a/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000..a852098 Binary files /dev/null and b/apps/geolibre-desktop/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png differ diff --git a/apps/geolibre-desktop/src-tauri/src/earth_engine_oauth.rs b/apps/geolibre-desktop/src-tauri/src/earth_engine_oauth.rs new file mode 100644 index 0000000..4e1859b --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/src/earth_engine_oauth.rs @@ -0,0 +1,405 @@ +use serde::{Deserialize, Serialize}; +use std::{ + collections::HashMap, + io::ErrorKind, + io::{BufRead, BufReader, Read, Write}, + net::{TcpListener, TcpStream}, + sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + Arc, Mutex, + }, + thread, + time::{SystemTime, UNIX_EPOCH}, +}; + +const OAUTH_HOST: &str = "127.0.0.1"; +const OAUTH_PORT: u16 = 5173; +const AUTH_PATH: &str = "/__geolibre_ee_auth"; +const TOKEN_PATH: &str = "/__geolibre_ee_token"; + +#[derive(Default)] +pub struct EarthEngineOAuthState { + counter: AtomicU64, + server_started: AtomicBool, + tokens: Arc>>, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +pub struct EarthEngineOAuthStart { + url: String, + state: String, +} + +#[derive(Clone, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct EarthEngineOAuthToken { + state: String, + access_token: Option, + token_type: Option, + expires_in: Option, + error: Option, +} + +#[tauri::command] +pub fn start_earth_engine_oauth( + client_id: String, + state: tauri::State<'_, EarthEngineOAuthState>, +) -> Result { + let client_id = client_id.trim(); + if client_id.is_empty() { + return Err("Earth Engine OAuth client ID is required.".to_string()); + } + + ensure_oauth_server(&state)?; + + let counter = state.counter.fetch_add(1, Ordering::Relaxed); + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|error| error.to_string())? + .as_millis(); + let state_id = format!("geolibre-{now}-{counter}"); + let url = format!( + "http://localhost:{OAUTH_PORT}{AUTH_PATH}?client_id={}&state={}", + url_encode(client_id), + url_encode(&state_id), + ); + + Ok(EarthEngineOAuthStart { + url, + state: state_id, + }) +} + +#[tauri::command] +pub fn poll_earth_engine_oauth( + state_id: String, + state: tauri::State<'_, EarthEngineOAuthState>, +) -> Result, String> { + let mut tokens = state.tokens.lock().map_err(|error| error.to_string())?; + Ok(tokens.remove(&state_id)) +} + +fn ensure_oauth_server(state: &EarthEngineOAuthState) -> Result<(), String> { + if state.server_started.load(Ordering::Acquire) { + return Ok(()); + } + if state + .server_started + .compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire) + .is_err() + { + return Ok(()); + } + + let listener = match TcpListener::bind((OAUTH_HOST, OAUTH_PORT)) { + Ok(listener) => listener, + Err(error) => { + state.server_started.store(false, Ordering::Release); + if error.kind() == ErrorKind::AddrInUse { + return Err(format!( + "Could not start Earth Engine OAuth helper on http://localhost:{OAUTH_PORT} because the port is already in use. Close any running GeoLibre dev server or other app using port {OAUTH_PORT}, then try again.", + )); + } + return Err(format!( + "Could not start Earth Engine OAuth helper on http://localhost:{OAUTH_PORT}: {error}", + )); + } + }; + let tokens = Arc::clone(&state.tokens); + + thread::spawn(move || { + for stream in listener.incoming().flatten() { + let tokens = Arc::clone(&tokens); + thread::spawn(move || handle_connection(stream, tokens)); + } + }); + + Ok(()) +} + +fn handle_connection( + mut stream: TcpStream, + tokens: Arc>>, +) { + let Ok((method, target, body)) = read_request(&stream) else { + let _ = write_response(&mut stream, 400, "text/plain", "Bad request"); + return; + }; + let (path, query) = split_target(&target); + + match (method.as_str(), path) { + ("GET", AUTH_PATH) => { + let params = query_params(query); + let client_id = params.get("client_id").cloned().unwrap_or_default(); + let state = params.get("state").cloned().unwrap_or_default(); + let _ = write_response( + &mut stream, + 200, + "text/html", + &auth_page(&client_id, &state), + ); + } + ("POST", TOKEN_PATH) => { + if let Ok(token) = serde_json::from_slice::(&body) { + if !token.state.is_empty() { + if let Ok(mut token_store) = tokens.lock() { + token_store.insert(token.state.clone(), token); + } + } + } + let _ = write_response(&mut stream, 204, "text/plain", ""); + } + ("OPTIONS", TOKEN_PATH) => { + let _ = write_response(&mut stream, 204, "text/plain", ""); + } + _ => { + let _ = write_response(&mut stream, 404, "text/plain", "Not found"); + } + } +} + +fn read_request(stream: &TcpStream) -> Result<(String, String, Vec), String> { + let mut reader = BufReader::new(stream.try_clone().map_err(|error| error.to_string())?); + let mut request_line = String::new(); + reader + .read_line(&mut request_line) + .map_err(|error| error.to_string())?; + let mut request_parts = request_line.split_whitespace(); + let method = request_parts.next().unwrap_or_default().to_string(); + let target = request_parts.next().unwrap_or_default().to_string(); + + let mut content_length = 0usize; + loop { + let mut line = String::new(); + reader + .read_line(&mut line) + .map_err(|error| error.to_string())?; + if line == "\r\n" || line.is_empty() { + break; + } + if let Some(value) = line.strip_prefix("Content-Length:") { + content_length = value.trim().parse().unwrap_or(0); + } else if let Some(value) = line.strip_prefix("content-length:") { + content_length = value.trim().parse().unwrap_or(0); + } + } + + let mut body = vec![0; content_length]; + if content_length > 0 { + reader + .read_exact(&mut body) + .map_err(|error| error.to_string())?; + } + + Ok((method, target, body)) +} + +fn split_target(target: &str) -> (&str, &str) { + target.split_once('?').unwrap_or((target, "")) +} + +fn query_params(query: &str) -> HashMap { + let mut params = HashMap::new(); + for pair in query.split('&').filter(|pair| !pair.is_empty()) { + let (key, value) = pair.split_once('=').unwrap_or((pair, "")); + params.insert(url_decode(key), url_decode(value)); + } + params +} + +fn write_response( + stream: &mut TcpStream, + status: u16, + content_type: &str, + body: &str, +) -> std::io::Result<()> { + let reason = match status { + 200 => "OK", + 204 => "No Content", + 400 => "Bad Request", + 404 => "Not Found", + _ => "OK", + }; + write!( + stream, + "HTTP/1.1 {status} {reason}\r\n\ + Content-Type: {content_type}; charset=utf-8\r\n\ + Content-Length: {}\r\n\ + Access-Control-Allow-Origin: http://localhost:{OAUTH_PORT}\r\n\ + Access-Control-Allow-Headers: content-type\r\n\ + Access-Control-Allow-Methods: POST, OPTIONS\r\n\ + Connection: close\r\n\ + \r\n\ + {body}", + body.len() + ) +} + +fn auth_page(client_id: &str, state: &str) -> String { + let client_id_json = serde_json::to_string(client_id).unwrap_or_else(|_| "\"\"".to_string()); + let state_json = serde_json::to_string(state).unwrap_or_else(|_| "\"\"".to_string()); + format!( + r#" + + + + + Earth Engine sign-in + + + +
+

Sign in to Earth Engine

+

Continue with Google to authorize GeoLibre Desktop to request Earth Engine map tiles.

+ +
+
+ + + +"# + ) +} + +fn url_encode(value: &str) -> String { + let mut encoded = String::new(); + for byte in value.bytes() { + match byte { + b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'-' | b'.' | b'_' | b'~' => { + encoded.push(byte as char); + } + _ => encoded.push_str(&format!("%{byte:02X}")), + } + } + encoded +} + +fn url_decode(value: &str) -> String { + let mut decoded = Vec::new(); + let bytes = value.as_bytes(); + let mut index = 0; + while index < bytes.len() { + if bytes[index] == b'%' && index + 2 < bytes.len() { + if let Ok(hex) = u8::from_str_radix(&value[index + 1..index + 3], 16) { + decoded.push(hex); + index += 3; + continue; + } + } + decoded.push(if bytes[index] == b'+' { + b' ' + } else { + bytes[index] + }); + index += 1; + } + String::from_utf8_lossy(&decoded).into_owned() +} diff --git a/apps/geolibre-desktop/src-tauri/src/lib.rs b/apps/geolibre-desktop/src-tauri/src/lib.rs new file mode 100644 index 0000000..00d12c1 --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/src/lib.rs @@ -0,0 +1,3083 @@ +mod earth_engine_oauth; +#[cfg(feature = "native-duckdb")] +mod native_duckdb; +#[cfg(not(feature = "native-duckdb"))] +mod native_duckdb { + #[tauri::command] + pub async fn count_native_vector_file_features( + _path: String, + _layer: Option, + ) -> Result { + Err("Native DuckDB is not enabled in this build.".to_string()) + } + + #[tauri::command] + pub async fn load_native_vector_file( + _path: String, + _layer: Option, + _override_source_crs: Option, + ) -> Result { + Err("Native DuckDB is not enabled in this build.".to_string()) + } +} + +use earth_engine_oauth::{ + poll_earth_engine_oauth, start_earth_engine_oauth, EarthEngineOAuthState, +}; +use flate2::read::{GzDecoder, ZlibDecoder}; +use rusqlite::{params, Connection, OpenFlags, OptionalExtension}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::collections::HashSet; +use std::env; +use std::fs::{self, File}; +use std::io::{Cursor, Read}; +use std::net::TcpListener; +use std::path::{Path, PathBuf}; +use std::process::{Child, Command, Stdio}; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Mutex; +use std::thread; +use std::time::Duration; +use tauri::Manager; + +// OAuth popups are a desktop-only, multi-window concept; Android/iOS have no +// equivalent, and `WebviewWindowBuilder::{on_new_window, window_features}` do +// not exist on the mobile runtime. +#[cfg(desktop)] +static POPUP_COUNTER: AtomicU64 = AtomicU64::new(0); + +const MARTIN_VERSION: &str = "martin-v1.10.1"; +const MARTIN_RELEASE_BASE_URL: &str = "https://github.com/maplibre/martin/releases/download"; +const MARTIN_START_ATTEMPTS: usize = 3; +const MARTIN_HEALTH_ATTEMPTS: usize = 30; +const SIDECAR_HEALTH_ATTEMPTS: usize = 180; +const SIDECAR_PORT: u16 = 8765; +// The desktop JupyterLab server for the Notebook panel. Loopback-bound and +// token-gated; uses its own uv project environment so it never disturbs the +// FastAPI sidecar's env. First start can be slow while uv syncs JupyterLab. +const JUPYTER_PORT: u16 = 8766; +// Polled once per second, so up to ~4 minutes — generous headroom for the +// first-run `uv sync` of JupyterLab on a cold cache. +const JUPYTER_HEALTH_ATTEMPTS: usize = 240; +const UV_INSTALL_BASE_URL: &str = "https://astral.sh/uv"; +const REMOTE_TILE_TIMEOUT_SECS: u64 = 8; +const REMOTE_TILE_CONNECT_TIMEOUT_SECS: u64 = 4; +const URL_RESOLVE_TIMEOUT_SECS: u64 = 15; + +#[cfg(unix)] +const SIGTERM: i32 = 15; +#[cfg(unix)] +const SIGKILL: i32 = 9; + +#[cfg(unix)] +unsafe extern "C" { + fn kill(pid: i32, sig: i32) -> i32; +} + +struct MartinServerState { + process: Mutex>, +} + +struct SidecarServerState { + process: Mutex>, +} + +struct JupyterServerState { + process: Mutex>, + // Token of the currently running server, so a reuse path can hand the same + // URL back without restarting. + token: Mutex>, + // Held for the whole of start_jupyter_server_blocking so two concurrent + // start calls can't both spawn on the same port (the loser would exit 1). + startup: Mutex<()>, +} + +struct MartinProcess { + child: Child, +} + +struct SidecarProcess { + child: Child, +} + +struct JupyterProcess { + child: Child, +} + +#[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +struct ExternalPluginManifest { + id: String, + name: String, + version: String, + entry: String, + #[serde(skip_serializing_if = "Option::is_none")] + description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + style: Option, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct ExternalPluginBundle { + archive_name: String, + manifest: ExternalPluginManifest, + entry_source: String, + style_source: Option, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct ExternalPluginBundleError { + archive_name: String, + message: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct ExternalPluginBundleLoadResult { + plugins_directories: Vec, + bundles: Vec, + errors: Vec, +} + +impl SidecarProcess { + fn terminate(&mut self) { + terminate_sidecar_child(&mut self.child); + } +} + +impl Drop for MartinProcess { + fn drop(&mut self) { + let _ = self.child.kill(); + let _ = self.child.wait(); + } +} + +impl Drop for SidecarProcess { + fn drop(&mut self) { + self.terminate(); + } +} + +impl JupyterProcess { + fn terminate(&mut self) { + // Same process-group teardown as the sidecar (the child is spawned with + // its own group, so this reaps `uv`/`jupyter`/kernel descendants). + terminate_sidecar_child(&mut self.child); + } +} + +impl Drop for JupyterProcess { + fn drop(&mut self) { + self.terminate(); + } +} + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + configure_linux_webkit(); + + tauri::Builder::default() + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_fs::init()) + // Must init after the fs plugin: it restores previously-granted fs + // scope (e.g. Browser-panel pinned folders) so they survive a restart. + // + // SECURITY / SCOPE NOTE (deliberate, maintainer-approved): this persists + // *every* dialog-granted fs scope for the life of the install, not just + // Browser-panel folder pins — "Open Vector File", "Open Raster", "Save + // Project As", etc. also extend fs scope to the picked path, and all of + // those now survive a restart. There is also no per-path "forget", so + // unpinning a folder in the Browser panel removes the UI pin but does + // not revoke its (persisted) read scope. Accepted for durable pins; + // revisit if a narrower per-source scope or a revoke path is wanted. + .plugin(tauri_plugin_persisted_scope::init()) + .plugin(tauri_plugin_http::init()) + .plugin(tauri_plugin_opener::init()) + .manage(EarthEngineOAuthState::default()) + .manage(MartinServerState { + process: Mutex::new(None), + }) + .manage(SidecarServerState { + process: Mutex::new(None), + }) + .manage(JupyterServerState { + process: Mutex::new(None), + token: Mutex::new(None), + startup: Mutex::new(()), + }) + .invoke_handler(tauri::generate_handler![ + close_oauth_popups, + native_duckdb::count_native_vector_file_features, + ensure_martin_binary, + fetch_url_bytes, + install_external_plugin_archive, + native_duckdb::load_native_vector_file, + load_external_plugin_bundles, + read_admin_profile, + read_env_vars, + read_local_file, + read_project_file, + read_shapefile_siblings, + resolve_url_redirect, + read_mbtiles_metadata, + read_mbtiles_tile, + start_martin_server, + stop_martin_server, + start_geolibre_sidecar, + stop_geolibre_sidecar, + start_jupyter_server, + stop_jupyter_server, + start_earth_engine_oauth, + poll_earth_engine_oauth + ]) + .setup(|app| { + create_main_window(app)?; + Ok(()) + }) + .run(tauri::generate_context!()) + .expect("error while running GeoLibre Desktop"); +} + +/// Whether `read_project_file` may read `path`: an absolute local path (POSIX +/// `/...` or a Windows drive-letter `C:\...`, never a UNC `\\host\share`), free +/// of `..` traversal, ending in a GeoLibre project extension — `.geolibre` or +/// `.geolibre.json`. These are the canonical formats `saveProject` writes and +/// `isGeoLibreProjectPath` recognizes in `tauri-io.ts`. +/// +/// Without this, the command was an arbitrary local-file reader: any webview JS +/// or loaded plugin could `invoke("read_project_file", { path: "~/.ssh/id_rsa" })` +/// and receive the contents. A bare `.json` extension is deliberately NOT +/// accepted: plenty of real secrets are JSON (GCP service-account keys, +/// `application_default_credentials.json`, editor/CLI configs with tokens), so +/// requiring the `.geolibre` marker keeps those out while still reading every +/// real project. Byte-oriented like `is_allowed_local_vector_path` so Windows +/// paths behave the same on any host. +pub(crate) fn is_allowed_project_path(path: &str) -> bool { + let bytes = path.as_bytes(); + let is_separator = |byte: u8| byte == b'/' || byte == b'\\'; + + // Reject UNC paths ("\\server\share" and "//server/share"). + if bytes.len() >= 2 && is_separator(bytes[0]) && is_separator(bytes[1]) { + return false; + } + + let is_posix_absolute = bytes.first() == Some(&b'/'); + let is_windows_drive = bytes.len() >= 3 + && bytes[0].is_ascii_alphabetic() + && bytes[1] == b':' + && is_separator(bytes[2]); + if !is_posix_absolute && !is_windows_drive { + return false; + } + + if path.split(['/', '\\']).any(|segment| segment == "..") { + return false; + } + + let lower = path.to_ascii_lowercase(); + lower.ends_with(".geolibre") || lower.ends_with(".geolibre.json") +} + +#[tauri::command] +fn read_project_file(path: String) -> Result { + if !is_allowed_project_path(&path) { + return Err(format!( + "Refusing to read \"{path}\": not an absolute local project file path" + )); + } + // Resolve symlinks and re-check the extension, so a symlink named + // `*.geolibre`/`*.geolibre.json` can't redirect the read to an arbitrary + // target (e.g. `~/notes.geolibre.json -> ~/.ssh/id_rsa`). Only the resolved + // extension is re-checked (not the full guard): `canonicalize` yields a + // `\\?\C:\…` verbatim path on Windows, which the UNC check would reject. + let canonical = + fs::canonicalize(&path).map_err(|error| format!("Could not read project file: {error}"))?; + let resolved = canonical.to_string_lossy().to_ascii_lowercase(); + if !(resolved.ends_with(".geolibre") || resolved.ends_with(".geolibre.json")) { + return Err(format!( + "Refusing to read \"{path}\": resolves to a non-project file" + )); + } + fs::read_to_string(&canonical).map_err(|error| format!("Could not read project file: {error}")) +} + +/// Local vector file extensions the restore path may re-read (lowercased, no +/// dot). Mirrors `VECTOR_FILE_DIALOG_EXTENSIONS` in `tauri-io.ts`; keep the two +/// in step. +// SYNC: VECTOR_FILE_DIALOG_EXTENSIONS in src/lib/tauri-io.ts — grep "SYNC:" to +// find the partner list and update both together. +const RESTORABLE_VECTOR_EXTENSIONS: [&str; 17] = [ + "geojson", + "json", + "gpkg", + "geoparquet", + "parquet", + "fgb", + "flatgeobuf", + "csv", + "tsv", + "kml", + "kmz", + "gml", + "gpx", + "dxf", + "tab", + "shp", + "zip", +]; + +/// Whether the renderer is permitted to re-read `path` through `read_local_file`: +/// an absolute local path (POSIX `/...` or a Windows drive-letter `C:\...`, never +/// a UNC `\\host\share`), free of `..` traversal segments, ending in a known +/// vector extension. +/// +/// This is a Rust-side backstop mirroring the frontend guard +/// (`isAbsoluteLocalPath` + `hasPathTraversal` + `isRestorableVectorPath` in +/// `tauri-io.ts`). It narrows the attack surface of a compromised webview or +/// rogue plugin: arbitrary system files (`/etc/passwd`, SSH keys, most shell and +/// app configs) are blocked. It does not make the command harmless — the +/// allowlist still includes broad extensions like `json`, so a script that knows +/// the path of a JSON-shaped secret could still read it — but it bounds reads to +/// the vector formats the restore path actually needs. The checks are +/// byte-oriented rather than `std::path` based so they behave identically for the +/// Windows-style paths a project may carry regardless of the host the binary +/// runs on. +pub(crate) fn is_allowed_local_vector_path(path: &str) -> bool { + // Absolute, non-UNC, no `..` traversal — split into `is_safe_absolute_path` + // so the security-relevant byte-parsing lives in one place rather than being + // duplicated. + if !is_safe_absolute_path(path) { + return false; + } + + // Known vector extension, case-insensitive, matching the JS + // `RESTORABLE_VECTOR_PATH` regex (built from `VECTOR_FILE_DIALOG_EXTENSIONS`). + // `rsplit_once` takes the text after the final dot without allocating. + let lower = path.to_ascii_lowercase(); + lower + .rsplit_once('.') + .is_some_and(|(_, extension)| RESTORABLE_VECTOR_EXTENSIONS.contains(&extension)) +} + +/// Read a local file's raw bytes so a project's file-referenced vector layers +/// can be re-read when the project is reopened. +/// +/// On reopen, a layer saved as a file reference carries only its absolute +/// `sourcePath` (stored in the `.geolibre.json`); that path was never picked or +/// dropped this session, so it sits outside the `fs` plugin's runtime scope and +/// the JS `readFile`/`readTextFile` reject it. This reads the file directly, +/// mirroring `read_project_file` (which bypasses the same scope to read the +/// project file itself). The path is validated here by +/// `is_allowed_local_vector_path` (absolute, no `..` traversal, known vector +/// extension) so the command cannot be abused to read arbitrary files even if +/// the frontend guard is bypassed. Bytes are returned as a raw IPC response (an +/// `ArrayBuffer` on the JS side) so a large GeoJSON does not pay the cost of a +/// JSON number array. +#[tauri::command] +fn read_local_file(path: String) -> Result { + if !is_allowed_local_vector_path(&path) { + return Err(format!( + "Refusing to read \"{path}\": not an absolute local vector file path" + )); + } + fs::read(&path) + .map(tauri::ipc::Response::new) + .map_err(|error| format!("Could not read local file: {error}")) +} + +/// Shapefile sidecar extensions read alongside a `.shp` (lowercased, no dot). +const SHAPEFILE_SIDECAR_EXTENSIONS: [&str; 16] = [ + "shx", "dbf", "prj", "cpg", "sbn", "sbx", "qix", "qpj", "cst", "aih", "ain", "atx", "ixs", + "mxs", "fbn", "fbx", +]; + +#[derive(Serialize)] +struct ShapefileSibling { + /// `.`, matching the `.shp` base name + /// so GDAL resolves the sidecar when reading the `.shp` directly. + name: String, + data: Vec, +} + +/// Read a shapefile's sidecar files (`.shx`, `.dbf`, `.prj`, `.cpg`, ...) sitting +/// next to the given `.shp`, so a loose `.shp` can be loaded without the user +/// selecting every component. +/// +/// The JS `fs` plugin can only read paths the user explicitly picked or dropped, +/// so it cannot reach a sidecar that was not selected; this reads them directly. +/// It is scoped to shapefile sidecar extensions, so it cannot read arbitrary +/// files. The directory is matched case-insensitively (handling `.SHX`/`.DBF` and +/// mixed-case base names), and each sidecar is returned under the `.shp`'s base +/// name with a lowercased extension so the registered names line up. Missing +/// siblings are skipped; an unreadable directory yields an empty list. +#[tauri::command] +fn read_shapefile_siblings(path: String) -> Result, String> { + let shp = Path::new(&path); + let Some(parent) = shp.parent() else { + return Ok(Vec::new()); + }; + let Some(stem) = shp.file_stem().and_then(|stem| stem.to_str()) else { + return Ok(Vec::new()); + }; + let entries = match fs::read_dir(parent) { + Ok(entries) => entries, + Err(_) => return Ok(Vec::new()), + }; + let mut siblings = Vec::new(); + for entry in entries.flatten() { + let entry_path = entry.path(); + if !entry_path.is_file() { + continue; + } + let (Some(entry_stem), Some(extension)) = ( + entry_path.file_stem().and_then(|stem| stem.to_str()), + entry_path + .extension() + .and_then(|extension| extension.to_str()), + ) else { + continue; + }; + if !entry_stem.eq_ignore_ascii_case(stem) { + continue; + } + let extension = extension.to_ascii_lowercase(); + if !SHAPEFILE_SIDECAR_EXTENSIONS.contains(&extension.as_str()) { + continue; + } + if let Ok(data) = fs::read(&entry_path) { + siblings.push(ShapefileSibling { + name: format!("{stem}.{extension}"), + data, + }); + } + } + Ok(siblings) +} + +/// Whether `path` is a safe absolute local path: an absolute POSIX (`/...`) or +/// Windows drive-letter (`C:\...`) path, never a UNC (`\\host\share`) share, and +/// free of `..` traversal segments. The shared absolute/non-UNC/no-traversal +/// guard behind [`is_allowed_local_vector_path`], kept separate so that +/// byte-parsing lives in one place rather than being duplicated per caller. +fn is_safe_absolute_path(path: &str) -> bool { + let bytes = path.as_bytes(); + let is_separator = |byte: u8| byte == b'/' || byte == b'\\'; + if bytes.len() >= 2 && is_separator(bytes[0]) && is_separator(bytes[1]) { + return false; + } + let is_posix_absolute = bytes.first() == Some(&b'/'); + let is_windows_drive = bytes.len() >= 3 + && bytes[0].is_ascii_alphabetic() + && bytes[1] == b':' + && is_separator(bytes[2]); + if !is_posix_absolute && !is_windows_drive { + return false; + } + !path.split(['/', '\\']).any(|segment| segment == "..") +} + +/// Read the optional admin UI-profile file (`/admin-profile.json`). +/// +/// Returns `Ok(None)` when the file is absent so a missing file is not an error; +/// administrators drop one in to pre-configure and optionally lock the UI profile +/// for a deployment. See `docs/ui-profiles.md`. +#[tauri::command] +fn read_admin_profile(app: tauri::AppHandle) -> Result, String> { + let config_dir = app + .path() + .app_config_dir() + .map_err(|error| format!("Could not resolve config directory: {error}"))?; + let path = config_dir.join("admin-profile.json"); + match fs::read_to_string(&path) { + Ok(contents) => Ok(Some(contents)), + Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(None), + Err(error) => Err(format!("Could not read admin profile: {error}")), + } +} + +/// The only environment variable names `read_env_vars` will ever return. This +/// is a hard, server-side boundary: external plugins and any other JavaScript +/// run in the same (unsandboxed) webview can `invoke("read_env_vars", …)` with +/// arbitrary names, so the allowlist cannot live in the frontend alone or a +/// malicious caller could exfiltrate unrelated shell secrets (SSH_AUTH_SOCK, +/// GITHUB_TOKEN, ambient cloud credentials, …). Kept in sync with +/// `OS_ENV_VAR_NAMES` in `apps/geolibre-desktop/src/lib/assistant/provider.ts` +/// — the `assistant-os-env` test parses this list and asserts the two match. +const ALLOWED_ENV_VARS: &[&str] = &[ + "GEOLIBRE_ASSISTANT_PROVIDER", + "GEOLIBRE_ASSISTANT_MODEL", + "GEMINI_API_KEY", + "GOOGLE_API_KEY", + "GOOGLE_GENAI_API_KEY", + "ANTHROPIC_API_KEY", + "OPENAI_API_KEY", + "OLLAMA_BASE_URL", + "OLLAMA_MODEL", + "OPENAI_COMPATIBLE_BASE_URL", + "OPENAI_COMPATIBLE_API_KEY", + "OPENAI_COMPATIBLE_MODEL", + "TAVILY_API_KEY", +]; + +/// Read the AI Assistant's allowlisted variables from the OS environment. +/// +/// Only names in `ALLOWED_ENV_VARS` that the caller requests are returned, +/// and only when present and non-empty, so the desktop app never leaks the full +/// process environment — nor any variable outside the allowlist — into the +/// webview. This lets the assistant source provider API keys from the user's +/// system/shell environment instead of the project file (issue #1141), keeping +/// secrets out of the saved `.geolibre.json`. +#[tauri::command] +fn read_env_vars(names: Vec) -> std::collections::HashMap { + names + .into_iter() + .filter(|name| ALLOWED_ENV_VARS.contains(&name.as_str())) + .filter_map(|name| { + let value = env::var(&name).ok()?; + let trimmed = value.trim(); + if trimmed.is_empty() { + None + } else { + Some((name, trimmed.to_string())) + } + }) + .collect() +} + +#[tauri::command] +fn close_oauth_popups(app: tauri::AppHandle) { + for window in app.webview_windows().values() { + let is_oauth_popup = window.label().starts_with("oauthPopup") + || window + .title() + .map(|title| { + title.contains("Earth Engine sign-in") + || title.contains("accounts.google.com") + || title.contains("Google") + }) + .unwrap_or(false); + if is_oauth_popup { + let _ = window.close(); + } + } +} + +/// Error surfaced when a fetch would reach a blocked address. +const SSRF_BLOCKED_MESSAGE: &str = + "Refusing to fetch a link-local, unspecified, or multicast address"; + +/// Whether an IP sits in a range a webview- or plugin-triggered fetch must not +/// reach. This is the SSRF guard for [`fetch_url_bytes`] and +/// [`resolve_url_redirect`]: those commands issue requests from the desktop +/// process's network position and hand the body/redirect back to the webview, so +/// without this a rogue plugin could read cloud metadata (169.254.169.254) — the +/// classic SSRF target — that browser `fetch` cannot reach because of CORS. +/// +/// Loopback and private/LAN ranges are deliberately NOT blocked: the app is +/// built to load XYZ tiles, COGs, and PMTiles from a user's own +/// `http://localhost:` or LAN dev server (issue #387), and these commands +/// are the desktop fetch path for that data. Blocking them would break a +/// documented workflow. Sensitive loopback services (the Python sidecar, the +/// Jupyter server) are individually token-gated, so the residual reachability of +/// loopback/LAN is acceptable; link-local/metadata, which has no such guard, is +/// blocked. +fn is_disallowed_ip(ip: std::net::IpAddr) -> bool { + use std::net::IpAddr; + match ip { + IpAddr::V4(v4) => { + v4.is_link_local() // 169.254.0.0/16 (incl. cloud metadata) + || v4.is_unspecified() // 0.0.0.0 + || v4.is_broadcast() + || v4.is_multicast() + } + IpAddr::V6(v6) => { + if let Some(mapped) = v6.to_ipv4_mapped() { + return is_disallowed_ip(IpAddr::V4(mapped)); + } + let segments = v6.segments(); + // Deprecated IPv4-compatible form `::a.b.c.d` (all-zero 96-bit + // prefix, no `ffff` marker, and not `::`/`::1`). `to_ipv4_mapped` + // only covers `::ffff:a.b.c.d`, so classify the embedded IPv4 here + // too — otherwise `::169.254.169.254` would slip past the guard. + if segments[..6].iter().all(|&s| s == 0) && !(segments[6] == 0 && segments[7] <= 1) { + let embedded = std::net::Ipv4Addr::new( + (segments[6] >> 8) as u8, + (segments[6] & 0xff) as u8, + (segments[7] >> 8) as u8, + (segments[7] & 0xff) as u8, + ); + return is_disallowed_ip(IpAddr::V4(embedded)); + } + // Not handled: 6to4 (2002::/16) and Teredo (2001::/32) also embed an + // IPv4 address, so e.g. 2002:a9fe:a9fe:: could reach 169.254.169.254. + // These transition mechanisms are effectively defunct and unrouted on + // modern hosts, so the practical risk is negligible; noted so this + // isn't mistaken for full coverage of every IPv4-embedding form. + v6.is_unspecified() + || v6.is_multicast() + // fe80::/10 link-local + || (segments[0] & 0xffc0) == 0xfe80 + } + } +} + +/// Reject a parsed URL that is non-HTTP(S) or whose host resolves to a +/// private/loopback/link-local address. Hostname hosts are resolved and rejected +/// if *any* resolved address is disallowed, so a name that points at an internal +/// IP cannot slip through. +fn url_is_fetchable(url: &reqwest::Url) -> Result<(), String> { + use std::net::ToSocketAddrs; + match url.scheme() { + "http" | "https" => {} + other => return Err(format!("Unsupported URL scheme: {other}")), + } + let host = url + .host_str() + .ok_or_else(|| "URL has no host".to_string())?; + let bare = host.trim_start_matches('[').trim_end_matches(']'); + if let Ok(ip) = bare.parse::() { + return if is_disallowed_ip(ip) { + Err(SSRF_BLOCKED_MESSAGE.to_string()) + } else { + Ok(()) + }; + } + let port = url.port_or_known_default().unwrap_or(0); + let mut resolved_any = false; + for addr in (bare, port) + .to_socket_addrs() + .map_err(|error| format!("Could not resolve host {bare}: {error}"))? + { + resolved_any = true; + if is_disallowed_ip(addr.ip()) { + return Err(SSRF_BLOCKED_MESSAGE.to_string()); + } + } + if resolved_any { + Ok(()) + } else { + Err(format!("Could not resolve host {bare}")) + } +} + +/// Parse and SSRF-validate a URL string before a request is issued. +fn ensure_fetchable_url(url: &str) -> Result<(), String> { + let parsed = reqwest::Url::parse(url).map_err(|error| format!("Invalid URL: {error}"))?; + url_is_fetchable(&parsed) +} + +/// A redirect policy that re-applies [`url_is_fetchable`] to every hop, so a +/// public URL that 3xx-redirects to an internal address is not followed. +fn guarded_redirect_policy() -> reqwest::redirect::Policy { + reqwest::redirect::Policy::custom(|attempt| { + if attempt.previous().len() >= 10 { + return attempt.stop(); + } + match url_is_fetchable(attempt.url()) { + Ok(()) => attempt.follow(), + Err(_) => attempt.stop(), + } + }) +} + +/// A DNS resolver that drops any address in a blocked range, so reqwest connects +/// only to IPs that passed [`is_disallowed_ip`]. +/// +/// [`url_is_fetchable`] validates the host *before* the request, but reqwest +/// re-resolves the name when it opens the connection, so a check-then-connect +/// gap remains: an attacker controlling DNS (short TTL) could answer the +/// pre-check with a public IP and the actual connection with `169.254.169.254`. +/// Enforcing the filter inside the resolver reqwest actually uses — for the +/// initial request and every redirect hop — closes that rebinding window. +struct GuardedDnsResolver; + +impl reqwest::dns::Resolve for GuardedDnsResolver { + fn resolve(&self, name: reqwest::dns::Name) -> reqwest::dns::Resolving { + use std::net::ToSocketAddrs; + Box::pin(async move { + let host = name.as_str().to_string(); + // std getaddrinfo blocks, but these are low-volume tile/URL fetches. + let resolved = (host.as_str(), 0u16).to_socket_addrs(); + let addrs: Vec = match resolved { + Ok(iter) => iter.filter(|addr| !is_disallowed_ip(addr.ip())).collect(), + Err(error) => { + return Err(Box::new(error) as Box); + } + }; + if addrs.is_empty() { + return Err(SSRF_BLOCKED_MESSAGE.into()); + } + Ok(Box::new(addrs.into_iter()) as reqwest::dns::Addrs) + }) + } +} + +/// Build a blocking HTTP client that enforces the SSRF guard at connect time +/// (via [`GuardedDnsResolver`]) and re-validates redirect hops. +fn guarded_http_client(timeout: Duration) -> Result { + reqwest::blocking::Client::builder() + .timeout(timeout) + .connect_timeout(Duration::from_secs(REMOTE_TILE_CONNECT_TIMEOUT_SECS)) + .redirect(guarded_redirect_policy()) + .dns_resolver(std::sync::Arc::new(GuardedDnsResolver)) + .user_agent("GeoLibre Desktop") + .build() + .map_err(|error| format!("Could not create HTTP client: {error}")) +} + +#[tauri::command] +async fn fetch_url_bytes(url: String) -> Result, String> { + tauri::async_runtime::spawn_blocking(move || fetch_url_bytes_blocking(url)) + .await + .map_err(|error| format!("Tile fetch task failed: {error}"))? +} + +fn fetch_url_bytes_blocking(url: String) -> Result, String> { + ensure_fetchable_url(&url)?; + + let client = guarded_http_client(Duration::from_secs(REMOTE_TILE_TIMEOUT_SECS))?; + + let response = client + .get(&url) + .send() + .map_err(|error| format!("Request failed: {error}"))?; + let status = response.status(); + if !status.is_success() { + return Err(format!("Request failed with status {status}")); + } + + response + .bytes() + .map(|bytes| bytes.to_vec()) + .map_err(|error| format!("Could not read response body: {error}")) +} + +/// Install a packaged plugin from a local `.zip` archive into GeoLibre's +/// app-data `plugins/` directory so it persists across restarts and is picked up +/// by the regular plugin scan. The archive is validated first (parsing +/// `plugin.json`, enforcing the manifest rules, and confirming the entry and +/// optional style are present and within the size limit), so only a loadable +/// plugin lands in the plugins directory. Returns the installed plugin id. +#[tauri::command] +async fn install_external_plugin_archive( + app: tauri::AppHandle, + source_path: String, +) -> Result { + tauri::async_runtime::spawn_blocking(move || { + install_external_plugin_archive_blocking(&app, source_path) + }) + .await + .map_err(|error| format!("Plugin install task failed: {error}"))? +} + +fn install_external_plugin_archive_blocking( + app: &tauri::AppHandle, + source_path: String, +) -> Result { + let source = PathBuf::from(&source_path); + if !is_zip_path(&source) { + return Err("Plugin file must be a .zip archive".to_string()); + } + + // Validate the archive up front by loading it the same way the startup scan + // does; this rejects a malformed manifest, a missing/oversized entry, or an + // unsafe asset path before any file is written. + let bundle = load_external_plugin_archive(&source, &source_path)?; + let plugin_id = bundle.manifest.id; + + let plugins_dir = app + .path() + .app_data_dir() + .map_err(|error| format!("Could not resolve app data directory: {error}"))? + .join("plugins"); + fs::create_dir_all(&plugins_dir) + .map_err(|error| format!("Could not create plugins directory: {error}"))?; + + let destination = plugins_dir.join(plugin_archive_file_name(&plugin_id)); + + // Reinstalling from a file already inside the plugins directory (e.g. the + // user re-selects the installed copy) would otherwise truncate the source + // mid-copy. Treat the no-op copy as a successful install. + let same_file = source + .canonicalize() + .ok() + .zip(destination.canonicalize().ok()) + .map(|(from, to)| from == to) + .unwrap_or(false); + if !same_file { + fs::copy(&source, &destination) + .map_err(|error| format!("Could not install plugin archive: {error}"))?; + } + + Ok(plugin_id) +} + +/// Build a filesystem-safe `.zip` name for an installed plugin archive. +/// +/// The manifest `id` is validated for emptiness and whitespace but not for +/// filesystem safety, so any character outside `[A-Za-z0-9._-]` is replaced with +/// `_` and leading dots are stripped to keep the name from escaping the plugins +/// directory or producing a hidden file. Using the id as the file name gives a +/// reinstall natural overwrite semantics. +fn plugin_archive_file_name(id: &str) -> String { + let sanitized: String = id + .chars() + .map(|character| { + if character.is_ascii_alphanumeric() + || character == '-' + || character == '_' + || character == '.' + { + character + } else { + '_' + } + }) + .collect(); + let trimmed = sanitized.trim_start_matches('.'); + let safe = if trimmed.is_empty() { + "plugin" + } else { + trimmed + }; + format!("{safe}.zip") +} + +#[tauri::command] +async fn load_external_plugin_bundles( + app: tauri::AppHandle, + additional_plugin_directories: Vec, +) -> Result { + tauri::async_runtime::spawn_blocking(move || { + load_external_plugin_bundles_blocking(&app, additional_plugin_directories) + }) + .await + .map_err(|error| format!("External plugin scan task failed: {error}"))? +} + +fn load_external_plugin_bundles_blocking( + app: &tauri::AppHandle, + additional_plugin_directories: Vec, +) -> Result { + let plugins_dir = app + .path() + .app_data_dir() + .map_err(|error| format!("Could not resolve app data directory: {error}"))? + .join("plugins"); + + fs::create_dir_all(&plugins_dir) + .map_err(|error| format!("Could not create plugins directory: {error}"))?; + + let mut plugin_dirs = Vec::new(); + let mut seen_dirs = HashSet::new(); + for directory in additional_plugin_directories { + let directory = directory.trim(); + if directory.is_empty() { + continue; + } + let path = PathBuf::from(directory); + let key = normalize_path_key(&path); + if seen_dirs.insert(key) { + plugin_dirs.push(path); + } + } + if seen_dirs.insert(normalize_path_key(&plugins_dir)) { + plugin_dirs.push(plugins_dir); + } + + let mut bundles = Vec::new(); + let mut errors = Vec::new(); + for plugin_dir in &plugin_dirs { + scan_external_plugin_directory(plugin_dir, &mut bundles, &mut errors); + } + + Ok(ExternalPluginBundleLoadResult { + plugins_directories: plugin_dirs + .iter() + .map(|path| path.to_string_lossy().to_string()) + .collect(), + bundles, + errors, + }) +} + +fn normalize_path_key(path: &Path) -> String { + // Canonicalize so symlinks and case differences on case-insensitive file + // systems (Windows, macOS) dedupe to one key; fall back to the raw path + // when it does not exist yet. + let canonical = path.canonicalize().unwrap_or_else(|_| path.to_path_buf()); + canonical.to_string_lossy().replace('\\', "/") +} + +fn scan_external_plugin_directory( + plugin_dir: &Path, + bundles: &mut Vec, + errors: &mut Vec, +) { + if !plugin_dir.exists() { + // User-configured development directories may not exist yet; the app + // data plugins directory is always created before scanning. Skip + // silently instead of warning on every startup. + return; + } + + if plugin_dir.join("plugin.json").is_file() { + let bundle_name = plugin_dir.to_string_lossy().to_string(); + match load_external_plugin_directory(plugin_dir, &bundle_name) { + Ok(bundle) => bundles.push(bundle), + Err(message) => errors.push(ExternalPluginBundleError { + archive_name: bundle_name, + message, + }), + } + return; + } + + let mut entries = match fs::read_dir(plugin_dir) { + Ok(entries) => entries.filter_map(Result::ok).collect::>(), + Err(error) => { + errors.push(ExternalPluginBundleError { + archive_name: plugin_dir.to_string_lossy().to_string(), + message: format!("Could not read plugins directory: {error}"), + }); + return; + } + }; + entries.sort_by_key(|entry| entry.file_name().to_string_lossy().to_string()); + + for entry in entries { + let path = entry.path(); + // Resolve through path metadata rather than the directory entry so + // symlinked zips and plugin directories are followed, not skipped. + let metadata = match path.metadata() { + Ok(metadata) => metadata, + Err(error) => { + errors.push(ExternalPluginBundleError { + archive_name: path.to_string_lossy().to_string(), + message: format!("Could not inspect plugin entry: {error}"), + }); + continue; + } + }; + + if metadata.is_file() && is_zip_path(&path) { + let bundle_name = path.to_string_lossy().to_string(); + match load_external_plugin_archive(&path, &bundle_name) { + Ok(bundle) => bundles.push(bundle), + Err(message) => errors.push(ExternalPluginBundleError { + archive_name: bundle_name, + message, + }), + } + } else if metadata.is_dir() && path.join("plugin.json").is_file() { + let bundle_name = path.to_string_lossy().to_string(); + match load_external_plugin_directory(&path, &bundle_name) { + Ok(bundle) => bundles.push(bundle), + Err(message) => errors.push(ExternalPluginBundleError { + archive_name: bundle_name, + message, + }), + } + } + } +} + +fn is_zip_path(path: &Path) -> bool { + path.extension() + .and_then(|extension| extension.to_str()) + .is_some_and(|extension| extension.eq_ignore_ascii_case("zip")) +} + +fn load_external_plugin_archive( + path: &Path, + archive_name: &str, +) -> Result { + let file = File::open(path).map_err(|error| format!("Could not open zip: {error}"))?; + let mut archive = + zip::ZipArchive::new(file).map_err(|error| format!("Could not read zip: {error}"))?; + // Tolerate a wrapping folder (`my-plugin/plugin.json`) as produced by zipping + // a plugin directory, not just a root `plugin.json`. The entry/style paths + // then resolve against the manifest's own directory. + let manifest_path = find_zip_manifest_path(&archive) + .ok_or_else(|| "Plugin archive is missing a plugin.json".to_string())?; + let prefix = manifest_path + .strip_suffix("plugin.json") + .unwrap_or("") + .to_string(); + let manifest_text = read_zip_text_entry(&mut archive, &manifest_path, "plugin manifest")?; + let manifest: ExternalPluginManifest = serde_json::from_str(&manifest_text) + .map_err(|error| format!("Could not parse plugin.json: {error}"))?; + validate_external_plugin_manifest(&manifest)?; + + let entry_source = read_zip_text_entry( + &mut archive, + &format!("{prefix}{}", manifest.entry), + "plugin entry", + )?; + let style_source = match manifest.style.as_deref() { + Some(style) => Some(read_zip_text_entry( + &mut archive, + &format!("{prefix}{style}"), + "plugin style", + )?), + None => None, + }; + + Ok(ExternalPluginBundle { + archive_name: archive_name.to_string(), + manifest, + entry_source, + style_source, + }) +} + +/// Find plugin.json inside a zip, tolerating a single wrapping folder. Prefers a +/// root `plugin.json`, otherwise returns the shallowest `*/plugin.json`, ignoring +/// the `__MACOSX/` metadata folder macOS adds to archives. Returns the manifest's +/// full path within the archive, or None when no plugin.json is present. +fn find_zip_manifest_path(archive: &zip::ZipArchive) -> Option { + let names: Vec<&str> = archive.file_names().collect(); + if names.iter().any(|name| *name == "plugin.json") { + return Some("plugin.json".to_string()); + } + let mut best: Option<&str> = None; + let mut best_depth = usize::MAX; + for name in names { + if name.starts_with("__MACOSX/") || !name.ends_with("/plugin.json") { + continue; + } + let depth = name.matches('/').count(); + if depth < best_depth || (depth == best_depth && best.is_none_or(|b| name < b)) { + best = Some(name); + best_depth = depth; + } + } + best.map(str::to_string) +} + +fn load_external_plugin_directory( + path: &Path, + archive_name: &str, +) -> Result { + let manifest_text = read_fs_text_entry(path, "plugin.json", "plugin manifest")?; + let manifest: ExternalPluginManifest = serde_json::from_str(&manifest_text) + .map_err(|error| format!("Could not parse plugin.json: {error}"))?; + validate_external_plugin_manifest(&manifest)?; + + let entry_source = read_fs_text_entry(path, &manifest.entry, "plugin entry")?; + let style_source = match manifest.style.as_deref() { + Some(style) => Some(read_fs_text_entry(path, style, "plugin style")?), + None => None, + }; + + Ok(ExternalPluginBundle { + archive_name: archive_name.to_string(), + manifest, + entry_source, + style_source, + }) +} + +fn read_fs_text_entry(root: &Path, entry_name: &str, label: &str) -> Result { + let entry_path = root.join(entry_name); + if !entry_path.is_file() { + return Err(format!( + "Could not read {label} '{entry_name}': file does not exist" + )); + } + + let file = File::open(&entry_path) + .map_err(|error| format!("Could not read {label} '{entry_name}': {error}"))?; + let mut text = String::new(); + file.take(MAX_PLUGIN_ENTRY_BYTES + 1) + .read_to_string(&mut text) + .map_err(|error| format!("Could not read {label} '{entry_name}' as UTF-8: {error}"))?; + if text.len() as u64 > MAX_PLUGIN_ENTRY_BYTES { + return Err(format!( + "{label} '{entry_name}' exceeds the {}-MB size limit", + MAX_PLUGIN_ENTRY_BYTES / (1024 * 1024) + )); + } + Ok(text) +} + +const MAX_PLUGIN_ENTRY_BYTES: u64 = 50 * 1024 * 1024; + +fn read_zip_text_entry( + archive: &mut zip::ZipArchive, + entry_name: &str, + label: &str, +) -> Result { + let entry = archive + .by_name(entry_name) + .map_err(|error| format!("Could not read {label} '{entry_name}': {error}"))?; + if entry.is_dir() { + return Err(format!("{label} '{entry_name}' must be a file")); + } + + // Cap the actual bytes read instead of trusting the zip header size, + // which a hand-crafted archive can spoof. + let mut text = String::new(); + entry + .take(MAX_PLUGIN_ENTRY_BYTES + 1) + .read_to_string(&mut text) + .map_err(|error| format!("Could not read {label} '{entry_name}' as UTF-8: {error}"))?; + if text.len() as u64 > MAX_PLUGIN_ENTRY_BYTES { + return Err(format!( + "{label} '{entry_name}' exceeds the {}-MB size limit", + MAX_PLUGIN_ENTRY_BYTES / (1024 * 1024) + )); + } + Ok(text) +} + +fn validate_external_plugin_manifest(manifest: &ExternalPluginManifest) -> Result<(), String> { + validate_required_manifest_string("id", &manifest.id)?; + validate_required_manifest_string("name", &manifest.name)?; + validate_required_manifest_string("version", &manifest.version)?; + validate_required_manifest_string("entry", &manifest.entry)?; + validate_external_plugin_path("entry", &manifest.entry)?; + if !manifest.entry.ends_with(".js") && !manifest.entry.ends_with(".mjs") { + return Err("entry must point to a .js or .mjs file".to_string()); + } + + if let Some(description) = manifest.description.as_deref() { + validate_optional_manifest_string("description", description)?; + } + if let Some(style) = manifest.style.as_deref() { + validate_optional_manifest_string("style", style)?; + validate_external_plugin_path("style", style)?; + if !style.ends_with(".css") { + return Err("style must point to a .css file".to_string()); + } + } + + Ok(()) +} + +fn validate_required_manifest_string(field: &str, value: &str) -> Result<(), String> { + if value.trim().is_empty() { + return Err(format!("{field} must not be empty")); + } + if value.trim() != value { + return Err(format!( + "{field} must not have leading or trailing whitespace" + )); + } + Ok(()) +} + +fn validate_optional_manifest_string(field: &str, value: &str) -> Result<(), String> { + if value.trim() != value { + return Err(format!( + "{field} must not have leading or trailing whitespace" + )); + } + Ok(()) +} + +fn validate_external_plugin_path(field: &str, value: &str) -> Result<(), String> { + if value.starts_with('/') { + return Err(format!("{field} must be a relative path")); + } + if value.contains('\\') { + return Err(format!("{field} must use forward slashes")); + } + if value.contains(':') { + return Err(format!( + "{field} must not contain drive letters or ':' characters" + )); + } + if value + .split('/') + .any(|part| part.is_empty() || part == "." || part == "..") + { + return Err(format!( + "{field} must not contain empty, '.', or '..' segments" + )); + } + Ok(()) +} + +#[tauri::command] +async fn resolve_url_redirect(url: String) -> Result { + tauri::async_runtime::spawn_blocking(move || resolve_url_redirect_blocking(url)) + .await + .map_err(|error| format!("URL resolve task failed: {error}"))? +} + +fn resolve_url_redirect_blocking(url: String) -> Result { + ensure_fetchable_url(&url)?; + + let client = guarded_http_client(Duration::from_secs(URL_RESOLVE_TIMEOUT_SECS))?; + + if let Ok(head_response) = client.head(&url).send() { + if has_xyz_placeholders(head_response.url().as_str()) { + return Ok(head_response.url().to_string()); + } + } + + let response = client + .get(&url) + .header("accept", "application/json, text/plain;q=0.9, */*;q=0.8") + .send() + .map_err(|error| format!("Request failed: {error}"))?; + if has_xyz_placeholders(response.url().as_str()) { + return Ok(response.url().to_string()); + } + + let body = response + .text() + .map_err(|error| format!("Could not read response body: {error}"))?; + + resolved_url_from_body(&body).ok_or_else(|| "Could not resolve URL".to_string()) +} + +fn has_xyz_placeholders(url: &str) -> bool { + let normalized = url.to_ascii_lowercase(); + (normalized.contains("{z}") || normalized.contains("%7bz%7d")) + && (normalized.contains("{x}") || normalized.contains("%7bx%7d")) + && (normalized.contains("{y}") || normalized.contains("%7by%7d")) +} + +fn resolved_url_from_body(body: &str) -> Option { + let trimmed = body.trim(); + if trimmed.starts_with("https://") || trimmed.starts_with("http://") { + return Some(trimmed.to_string()); + } + + let value: Value = serde_json::from_str(trimmed).ok()?; + resolved_url_from_json(&value) +} + +fn resolved_url_from_json(value: &Value) -> Option { + if let Some(url) = value.as_str() { + return http_url(url); + } + + let object = value.as_object()?; + for key in ["url", "tileUrl", "tile_url"] { + if let Some(url) = object.get(key).and_then(Value::as_str).and_then(http_url) { + return Some(url); + } + } + + object + .get("tiles") + .and_then(Value::as_array) + .and_then(|tiles| tiles.first()) + .and_then(Value::as_str) + .and_then(http_url) +} + +fn http_url(url: &str) -> Option { + if url.starts_with("https://") || url.starts_with("http://") { + Some(url.to_string()) + } else { + None + } +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct MartinBinaryInfo { + path: String, + downloaded: bool, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct MartinServerInfo { + base_url: String, + binary_path: String, + port: u16, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct SidecarServerInfo { + base_url: String, + port: u16, + /// Per-launch bearer token the frontend must send on every sidecar request + /// (see [`sidecar_token`]). + token: String, +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct JupyterServerInfo { + url: String, + port: u16, + token: String, +} + +#[tauri::command] +fn ensure_martin_binary(app: tauri::AppHandle) -> Result { + ensure_martin_binary_path(&app) +} + +#[tauri::command] +async fn start_martin_server( + app: tauri::AppHandle, + connection_string: String, + default_srid: Option, +) -> Result { + tauri::async_runtime::spawn_blocking(move || { + start_martin_server_blocking(app, connection_string, default_srid) + }) + .await + .map_err(|error| format!("Could not join Martin startup task: {error}"))? +} + +fn start_martin_server_blocking( + app: tauri::AppHandle, + connection_string: String, + default_srid: Option, +) -> Result { + if connection_string.trim().is_empty() { + return Err("Enter a PostgreSQL connection string.".to_string()); + } + + let binary = ensure_martin_binary_path(&app)?; + let state = app.state::(); + { + let process = state + .process + .lock() + .map_err(|_| "Could not lock Martin process state.".to_string())?; + if process.is_some() { + return Err( + "A Martin server is already running. Stop it before starting a new one." + .to_string(), + ); + } + } + + let mut last_error = "Could not start Martin.".to_string(); + for _ in 0..MARTIN_START_ATTEMPTS { + match spawn_martin_server( + &binary.path, + connection_string.trim(), + default_srid.as_deref(), + ) { + Ok(info) => { + let mut process = state + .process + .lock() + .map_err(|_| "Could not lock Martin process state.".to_string())?; + if process.is_some() { + drop(info.process); + return Err( + "A Martin server is already running. Stop it before starting a new one." + .to_string(), + ); + } + *process = Some(info.process); + return Ok(MartinServerInfo { + base_url: info.base_url, + binary_path: binary.path, + port: info.port, + }); + } + Err(error) => { + last_error = error; + } + } + } + + Err(last_error) +} + +#[tauri::command] +fn stop_martin_server(state: tauri::State) -> Result<(), String> { + let mut process = state + .process + .lock() + .map_err(|_| "Could not lock Martin process state.".to_string())?; + *process = None; + Ok(()) +} + +#[tauri::command] +async fn start_geolibre_sidecar(app: tauri::AppHandle) -> Result { + tauri::async_runtime::spawn_blocking(move || start_geolibre_sidecar_blocking(app)) + .await + .map_err(|error| format!("Could not join sidecar startup task: {error}"))? +} + +fn start_geolibre_sidecar_blocking(app: tauri::AppHandle) -> Result { + let base_url = sidecar_base_url(); + let state = app.state::(); + { + let mut process = state + .process + .lock() + .map_err(|_| "Could not lock sidecar process state.".to_string())?; + if let Some(sidecar) = process.as_mut() { + if sidecar + .child + .try_wait() + .map_err(|error| format!("Could not inspect sidecar process: {error}"))? + .is_none() + { + return Ok(SidecarServerInfo { + base_url, + port: SIDECAR_PORT, + token: sidecar_token().to_string(), + }); + } + *process = None; + } + } + + if sidecar_health_is_ready(&base_url) { + if sidecar_accepts_token(&base_url, sidecar_token()) { + return Ok(SidecarServerInfo { + base_url, + port: SIDECAR_PORT, + token: sidecar_token().to_string(), + }); + } + // A sidecar is listening but rejects this session's token — an orphan + // from a previous launch still holding the port. We can't reclaim it + // (no child handle here, and /shutdown is token-protected), so fail with + // a clear message rather than handing back a token that 401s every call. + return Err( + "A GeoLibre processing server from a previous session is still \ + running on port 8765 but does not accept this session's token. \ + Quit any stray GeoLibre processes and try again." + .to_string(), + ); + } + + let uv = ensure_managed_uv(&app)?; + let project_dir = sidecar_project_dir(&app)?; + let runtime_dir = app + .path() + .app_data_dir() + .map_err(|error| format!("Could not resolve app data directory: {error}"))? + .join("runtime"); + + let mut command = Command::new(&uv); + command + .arg("run") + .arg("--project") + .arg(&project_dir) + // The AI segmentation `/ml` endpoints proxy to samgeo-api from inside + // this main sidecar process and need `httpx`, which lives in the `ml` + // extra. Unlike whitebox/conversion (separate managed venvs), ml has no + // lazy bootstrap, so the extra must be synced into the sidecar env here. + .arg("--extra") + .arg("ml") + .arg("uvicorn") + .arg("geolibre_server.app.main:app") + .arg("--host") + .arg("127.0.0.1") + .arg("--port") + .arg(SIDECAR_PORT.to_string()) + .current_dir(&project_dir) + .env("GEOLIBRE_UV", &uv) + .env("GEOLIBRE_SIDECAR_TOKEN", sidecar_token()) + .env("GEOLIBRE_RUNTIME_DIR", &runtime_dir) + .env("UV_CACHE_DIR", runtime_dir.join("uv-cache")) + .env("UV_PYTHON_INSTALL_DIR", runtime_dir.join("uv-python")) + .env("UV_PROJECT_ENVIRONMENT", runtime_dir.join("sidecar-server")) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + configure_sidecar_process(&mut command); + + let mut child = command + .spawn() + .map_err(|error| format!("Could not start GeoLibre sidecar: {error}"))?; + + if let Err(error) = wait_for_sidecar_health(&base_url, &mut child) { + terminate_sidecar_child(&mut child); + return Err(error); + } + + let _ = child.stdout.take(); + let _ = child.stderr.take(); + + let mut process = state + .process + .lock() + .map_err(|_| "Could not lock sidecar process state.".to_string())?; + if process.is_some() { + let mut duplicate = SidecarProcess { child }; + duplicate.terminate(); + } else { + *process = Some(SidecarProcess { child }); + } + + Ok(SidecarServerInfo { + base_url, + port: SIDECAR_PORT, + token: sidecar_token().to_string(), + }) +} + +#[tauri::command] +async fn stop_geolibre_sidecar(app: tauri::AppHandle) -> Result<(), String> { + tauri::async_runtime::spawn_blocking(move || stop_geolibre_sidecar_blocking(app)) + .await + .map_err(|error| format!("Could not join sidecar stop task: {error}"))? +} + +fn stop_geolibre_sidecar_blocking(app: tauri::AppHandle) -> Result<(), String> { + let state = app.state::(); + { + let mut process = state + .process + .lock() + .map_err(|_| "Could not lock sidecar process state.".to_string())?; + // SidecarProcess::Drop calls terminate() automatically, so taking the + // value out is enough to tear down the child. Calling terminate() here + // as well would double-signal the (possibly recycled) process group. + let taken = process.take(); + drop(process); // release the MutexGuard before the 250 ms SIGTERM grace + drop(taken); // terminate() runs here, outside the lock + } + + let base_url = sidecar_base_url(); + if sidecar_health_is_ready(&base_url) { + request_sidecar_shutdown(&base_url); + wait_for_sidecar_stop(&base_url); + } + if sidecar_health_is_ready(&base_url) { + terminate_sidecar_listeners_on_port(SIDECAR_PORT)?; + wait_for_sidecar_stop(&base_url); + } + if sidecar_health_is_ready(&base_url) { + return Err(format!( + "GeoLibre sidecar is still running on port {SIDECAR_PORT}." + )); + } + Ok(()) +} + +#[tauri::command] +async fn start_jupyter_server(app: tauri::AppHandle) -> Result { + tauri::async_runtime::spawn_blocking(move || start_jupyter_server_blocking(app)) + .await + .map_err(|error| format!("Could not join Jupyter startup task: {error}"))? +} + +fn start_jupyter_server_blocking(app: tauri::AppHandle) -> Result { + let state = app.state::(); + // Serialize the whole startup. Concurrent calls (e.g. React StrictMode + // double-invoking the Notebook panel's mount effect in dev) would otherwise + // both pass the reuse check below and both spawn on JUPYTER_PORT; with + // `--port-retries=0` the loser fails to bind and exits 1. The second caller + // blocks here, then finds the live process in the reuse check and returns it. + let _startup = state + .startup + .lock() + .map_err(|_| "Could not lock Jupyter startup.".to_string())?; + // Reuse an already-running server: hand back the same URL + token. + { + let mut process = state + .process + .lock() + .map_err(|_| "Could not lock Jupyter process state.".to_string())?; + if let Some(server) = process.as_mut() { + if server + .child + .try_wait() + .map_err(|error| format!("Could not inspect Jupyter process: {error}"))? + .is_none() + { + let token = state + .token + .lock() + .map_err(|_| "Could not lock Jupyter token.".to_string())? + .clone() + .unwrap_or_default(); + return Ok(JupyterServerInfo { + url: jupyter_base_url(), + port: JUPYTER_PORT, + token, + }); + } + *process = None; + } + } + + // A Jupyter server from a previous app session may still hold the port. We + // can't reuse it (its per-launch token is unknown to us), and because we + // spawn with `--ServerApp.port_retries=0`, a new server would fail to bind + // and exit 1 ("exited before it was ready") while the orphan lingers. Clear + // any stale listener and wait for the port to free before spawning. + let _ = terminate_jupyter_listeners_on_port(JUPYTER_PORT); + wait_for_port_free(JUPYTER_PORT); + + let uv = ensure_managed_uv(&app)?; + let project_dir = sidecar_project_dir(&app)?; + let config_path = project_dir.join("jupyter_server_config.py"); + let runtime_dir = app + .path() + .app_data_dir() + .map_err(|error| format!("Could not resolve app data directory: {error}"))? + .join("runtime"); + // Notebooks are saved here (the JupyterLab file browser root). + let notebooks_dir = runtime_dir.join("notebooks"); + let _ = fs::create_dir_all(¬ebooks_dir); + // Seed the starter Welcome notebook (the same one bundled into JupyterLite on + // web) on first run only, so we never clobber a user's edits. + let welcome_dest = notebooks_dir.join("Welcome.ipynb"); + if !welcome_dest.exists() { + let _ = fs::copy( + project_dir.join("notebook_examples").join("Welcome.ipynb"), + &welcome_dest, + ); + } + // Make the kernel-side `geolibre` client importable from any notebook: copy + // it out of the bundled backend resource into a dedicated lib dir placed on + // the kernel's PYTHONPATH (so `import geolibre` works regardless of where the + // notebook lives). + let lib_dir = runtime_dir.join("notebook-lib"); + let _ = fs::create_dir_all(&lib_dir); + let _ = fs::copy( + project_dir.join("notebook_client.py"), + lib_dir.join("geolibre.py"), + ); + let token = generate_jupyter_token(); + + let mut command = Command::new(&uv); + command + .arg("run") + .arg("--project") + .arg(&project_dir) + // The `notebook` extra carries JupyterLab. Synced into a dedicated + // project environment so it never disturbs the FastAPI sidecar's env + // (which syncs the `ml` extra). + .arg("--extra") + .arg("notebook") + .arg("jupyter") + .arg("lab") + .arg("--no-browser") + .arg(format!("--config={}", config_path.display())) + .arg("--ServerApp.ip=127.0.0.1") + .arg(format!("--ServerApp.port={JUPYTER_PORT}")) + // Fail fast instead of hopping to another port we'd never discover. + .arg("--ServerApp.port_retries=0") + .arg(format!("--ServerApp.root_dir={}", notebooks_dir.display())) + .arg(format!("--IdentityProvider.token={token}")) + .current_dir(&project_dir) + .env("GEOLIBRE_UV", &uv) + .env("GEOLIBRE_RUNTIME_DIR", &runtime_dir) + .env("UV_CACHE_DIR", runtime_dir.join("uv-cache")) + .env("UV_PYTHON_INSTALL_DIR", runtime_dir.join("uv-python")) + .env("UV_PROJECT_ENVIRONMENT", runtime_dir.join("jupyter-server")) + // Prepend the bundled `geolibre` client to the kernel import path, + // preserving any inherited PYTHONPATH rather than replacing it. + .env("PYTHONPATH", prepend_pythonpath(&lib_dir)) + .stdin(Stdio::null()) + // Inherit (don't capture) stdout/stderr. Unlike the sidecar's quiet + // uvicorn, `uv sync` of JupyterLab + JupyterLab's own startup write a lot; + // a captured 64 KB pipe we don't drain during the health wait would fill + // and block the child, so it would never become ready. Inheriting also + // surfaces the logs in the dev terminal for debugging. + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()); + configure_sidecar_process(&mut command); + + let mut child = command + .spawn() + .map_err(|error| format!("Could not start Jupyter server: {error}"))?; + + let base_url = jupyter_base_url(); + if let Err(error) = wait_for_jupyter_health(&base_url, &token, &mut child) { + terminate_sidecar_child(&mut child); + return Err(error); + } + + // We hold the startup lock, so no concurrent start could have stored a + // process; record ours as the live server. + *state + .process + .lock() + .map_err(|_| "Could not lock Jupyter process state.".to_string())? = + Some(JupyterProcess { child }); + *state + .token + .lock() + .map_err(|_| "Could not lock Jupyter token.".to_string())? = Some(token.clone()); + + Ok(JupyterServerInfo { + url: base_url, + port: JUPYTER_PORT, + token, + }) +} + +#[tauri::command] +async fn stop_jupyter_server(app: tauri::AppHandle) -> Result<(), String> { + tauri::async_runtime::spawn_blocking(move || stop_jupyter_server_blocking(app)) + .await + .map_err(|error| format!("Could not join Jupyter stop task: {error}"))? +} + +fn stop_jupyter_server_blocking(app: tauri::AppHandle) -> Result<(), String> { + let state = app.state::(); + { + let mut process = state + .process + .lock() + .map_err(|_| "Could not lock Jupyter process state.".to_string())?; + // JupyterProcess::Drop terminates the child's process group; taking the + // value out is enough (see stop_geolibre_sidecar_blocking). + let taken = process.take(); + drop(process); + drop(taken); + } + if let Ok(mut token) = state.token.lock() { + *token = None; + } + // Backstop: reap anything still bound to the port (no-op on non-unix and + // when nothing is listening). + terminate_jupyter_listeners_on_port(JUPYTER_PORT)?; + Ok(()) +} + +fn jupyter_base_url() -> String { + format!("http://127.0.0.1:{JUPYTER_PORT}") +} + +// Wait (briefly) until `port` can be bound, i.e. a just-terminated listener has +// fully released it. Binding then dropping leaves a small race window, but it is +// enough to avoid a `--port-retries=0` bind failure right after killing an orphan. +fn wait_for_port_free(port: u16) { + for _ in 0..20 { + if TcpListener::bind(("127.0.0.1", port)).is_ok() { + return; + } + thread::sleep(Duration::from_millis(100)); + } +} + +// Prepend `dir` to any inherited PYTHONPATH (platform separator), so a user's +// existing value is preserved rather than clobbered. +fn prepend_pythonpath(dir: &Path) -> String { + let dir = dir.display().to_string(); + match env::var("PYTHONPATH") { + Ok(existing) if !existing.is_empty() => { + let sep = if cfg!(windows) { ";" } else { ":" }; + format!("{dir}{sep}{existing}") + } + _ => dir, + } +} + +fn jupyter_health_is_ready( + client: &reqwest::blocking::Client, + base_url: &str, + token: &str, +) -> bool { + client + .get(format!("{base_url}/api/status")) + .header("Authorization", format!("token {token}")) + .send() + .map(|response| response.status().is_success()) + .unwrap_or(false) +} + +fn wait_for_jupyter_health(base_url: &str, token: &str, child: &mut Child) -> Result<(), String> { + // Build the HTTP client once and reuse it across all health polls (this loop + // runs up to JUPYTER_HEALTH_ATTEMPTS = 240 times). + let client = reqwest::blocking::Client::builder() + .timeout(Duration::from_millis(500)) + .build() + .map_err(|error| format!("Could not build HTTP client: {error}"))?; + for _ in 0..JUPYTER_HEALTH_ATTEMPTS { + if let Some(status) = child + .try_wait() + .map_err(|error| format!("Could not inspect Jupyter process: {error}"))? + { + // Output is inherited (visible in the terminal), not captured, so we + // surface only the exit status here. + return Err(format!( + "Jupyter server exited before it was ready (exit status: {status}). \ + Check the terminal for the Jupyter/uv startup output." + )); + } + + if jupyter_health_is_ready(&client, base_url, token) { + return Ok(()); + } + + thread::sleep(Duration::from_secs(1)); + } + + Err("Jupyter server did not become ready in time.".to_string()) +} + +// A loopback-bound, per-launch token for the desktop Jupyter server. It is the +// only barrier once the XSRF check is disabled for the embedded iframe (see +// jupyter_server_config.py), so use the OS CSPRNG (128 random bits) rather than +// anything derived from the clock/pid. +fn generate_jupyter_token() -> String { + let mut bytes = [0u8; 16]; + getrandom::fill(&mut bytes).expect("OS CSPRNG (getrandom) unavailable"); + let mut token = String::with_capacity(32); + for byte in bytes { + use std::fmt::Write; + let _ = write!(token, "{byte:02x}"); + } + token +} + +fn sidecar_base_url() -> String { + format!("http://127.0.0.1:{SIDECAR_PORT}") +} + +/// A per-launch shared secret the frontend must present on every sidecar +/// request. The sidecar binds loopback and is CORS-restricted, but neither stops +/// a cross-origin simple POST (CSRF) or a DNS-rebinding read; this token does. +/// +/// Generated once per desktop process (128 CSPRNG bits) and reused for the +/// process lifetime so the early-return paths of `start_geolibre_sidecar` (when +/// the sidecar is already running) hand back the same token that was injected +/// via `GEOLIBRE_SIDECAR_TOKEN` at spawn time. A sidecar started outside this +/// process (e.g. a `python -m` dev run) leaves the env var unset and simply does +/// not enforce the token, so those flows keep working. +fn sidecar_token() -> &'static str { + static TOKEN: std::sync::OnceLock = std::sync::OnceLock::new(); + TOKEN.get_or_init(generate_jupyter_token) +} + +fn sidecar_health_is_ready(base_url: &str) -> bool { + let client = reqwest::blocking::Client::builder() + .timeout(Duration::from_millis(500)) + .build(); + let Ok(client) = client else { + return false; + }; + client + .get(format!("{base_url}/health")) + .send() + .map(|response| response.status().is_success()) + .unwrap_or(false) +} + +/// Whether the sidecar already listening at `base_url` accepts `token`. +/// +/// `/health` is token-exempt, so it cannot reveal a token mismatch. This probes +/// `/algorithms` (a cheap authenticated endpoint) *with* the token so an orphan +/// sidecar from a previous launch — started with a different per-launch token, +/// and not killed because `Drop` doesn't run on `SIGKILL` — is detected rather +/// than silently 401ing every later request. A tokenless dev sidecar +/// (`GEOLIBRE_SIDECAR_TOKEN` unset) accepts any header and returns 200, so it is +/// still reused. +fn sidecar_accepts_token(base_url: &str, token: &str) -> bool { + let Ok(client) = reqwest::blocking::Client::builder() + .timeout(Duration::from_millis(500)) + .build() + else { + return false; + }; + client + .get(format!("{base_url}/algorithms")) + .header("x-geolibre-token", token) + .send() + .map(|response| response.status().is_success()) + .unwrap_or(false) +} + +fn request_sidecar_shutdown(base_url: &str) { + let client = reqwest::blocking::Client::builder() + .timeout(Duration::from_millis(500)) + .build(); + if let Ok(client) = client { + // /shutdown is token-protected (only /health is exempt), so attach this + // session's token. This shuts down a sidecar we started or adopted (same + // token); a true cross-launch orphan (different token) 401s and falls + // through to the force-kill path, as before. + let _ = client + .post(format!("{base_url}/shutdown")) + .header("x-geolibre-token", sidecar_token()) + .send(); + } +} + +fn wait_for_sidecar_stop(base_url: &str) { + for _ in 0..20 { + if !sidecar_health_is_ready(base_url) { + return; + } + thread::sleep(Duration::from_millis(100)); + } +} + +fn wait_for_sidecar_health(base_url: &str, child: &mut Child) -> Result<(), String> { + for _ in 0..SIDECAR_HEALTH_ATTEMPTS { + if let Some(status) = child + .try_wait() + .map_err(|error| format!("Could not inspect sidecar process: {error}"))? + { + let output = read_child_output(child); + return Err(if output.trim().is_empty() { + format!("GeoLibre sidecar exited before it was ready: {status}") + } else { + format!("GeoLibre sidecar exited before it was ready: {output}") + }); + } + + if sidecar_health_is_ready(base_url) { + return Ok(()); + } + + thread::sleep(Duration::from_secs(1)); + } + + Err("GeoLibre sidecar did not become ready in time.".to_string()) +} + +fn configure_sidecar_process(command: &mut Command) { + configure_sidecar_process_impl(command); +} + +#[cfg(unix)] +fn configure_sidecar_process_impl(command: &mut Command) { + use std::os::unix::process::CommandExt; + + command.process_group(0); +} + +#[cfg(not(unix))] +fn configure_sidecar_process_impl(_command: &mut Command) {} + +fn terminate_sidecar_child(child: &mut Child) { + terminate_sidecar_process_group(child); + let _ = child.kill(); + let _ = child.wait(); +} + +#[cfg(unix)] +fn terminate_sidecar_process_group(child: &mut Child) { + // Guard the negation: a PID that wrapped to a non-positive i32 would make + // `kill` target process group 0 (the caller's own group, including the + // Tauri parent) or overflow on i32::MIN. + let Some(process_group) = i32::try_from(child.id()) + .ok() + .filter(|pid| *pid > 0) + .and_then(|pid| pid.checked_neg()) + else { + return; + }; + let _ = unsafe { kill(process_group, SIGTERM) }; + thread::sleep(Duration::from_millis(250)); + let _ = unsafe { kill(process_group, SIGKILL) }; +} + +#[cfg(not(unix))] +fn terminate_sidecar_process_group(_child: &mut Child) {} + +#[cfg(target_os = "linux")] +fn terminate_sidecar_listeners_on_port(port: u16) -> Result<(), String> { + terminate_listeners_on_port(port, is_geolibre_sidecar_process) +} + +// Reap a stale Jupyter server that still holds the port (e.g. orphaned by a +// non-graceful exit of a previous app session). Recognized by its cmdline so we +// never touch an unrelated jupyter (the user's own JupyterHub, etc.). +#[cfg(target_os = "linux")] +fn terminate_jupyter_listeners_on_port(port: u16) -> Result<(), String> { + terminate_listeners_on_port(port, is_geolibre_jupyter_process) +} + +// Known gap: this is a no-op on macOS/Windows (the /proc-based listener lookup is +// Linux-only). The current session's child is still reaped on exit via +// JupyterProcess::Drop, but an orphan left by a *previous* crashed session can +// keep holding the port there, in which case the next launch fails to bind and +// surfaces "exited before it was ready". A cross-platform port-owner lookup +// (e.g. lsof on macOS) would be needed to close this. +#[cfg(not(target_os = "linux"))] +fn terminate_jupyter_listeners_on_port(_port: u16) -> Result<(), String> { + Ok(()) +} + +// Kill the processes listening on `port` that `is_ours` recognizes (SIGTERM then +// SIGKILL). The `is_ours` guard prevents killing an unrelated process that +// happens to hold the port. +#[cfg(target_os = "linux")] +fn terminate_listeners_on_port(port: u16, is_ours: fn(i32) -> bool) -> Result<(), String> { + let inodes = listening_tcp_inodes(port)?; + if inodes.is_empty() { + return Ok(()); + } + + let mut pids = HashSet::new(); + for entry in fs::read_dir("/proc").map_err(|error| format!("Could not read /proc: {error}"))? { + let entry = entry.map_err(|error| format!("Could not read /proc entry: {error}"))?; + let Some(pid) = entry + .file_name() + .to_str() + .and_then(|value| value.parse::().ok()) + else { + continue; + }; + if process_has_socket(pid, &inodes)? && is_ours(pid) { + pids.insert(pid); + } + } + + for pid in &pids { + terminate_pid(*pid, SIGTERM); + } + thread::sleep(Duration::from_millis(250)); + for pid in &pids { + terminate_pid(*pid, SIGKILL); + } + Ok(()) +} + +#[cfg(not(target_os = "linux"))] +fn terminate_sidecar_listeners_on_port(_port: u16) -> Result<(), String> { + Ok(()) +} + +#[cfg(target_os = "linux")] +fn listening_tcp_inodes(port: u16) -> Result, String> { + let mut inodes = HashSet::new(); + collect_listening_tcp_inodes("/proc/net/tcp", port, &mut inodes)?; + collect_listening_tcp_inodes("/proc/net/tcp6", port, &mut inodes)?; + Ok(inodes) +} + +#[cfg(target_os = "linux")] +fn collect_listening_tcp_inodes( + path: &str, + port: u16, + inodes: &mut HashSet, +) -> Result<(), String> { + let content = + fs::read_to_string(path).map_err(|error| format!("Could not read {path}: {error}"))?; + let expected_port = format!("{port:04X}"); + for line in content.lines().skip(1) { + let fields: Vec<&str> = line.split_whitespace().collect(); + if fields.len() <= 9 || fields[3] != "0A" { + continue; + } + let Some(local_port) = fields[1].rsplit_once(':').map(|(_, value)| value) else { + continue; + }; + if local_port.eq_ignore_ascii_case(&expected_port) { + inodes.insert(fields[9].to_string()); + } + } + Ok(()) +} + +#[cfg(target_os = "linux")] +fn process_has_socket(pid: i32, inodes: &HashSet) -> Result { + let fd_dir = format!("/proc/{pid}/fd"); + let Ok(entries) = fs::read_dir(&fd_dir) else { + return Ok(false); + }; + for entry in entries { + let Ok(entry) = entry else { + continue; // process may have exited between the /proc scan and this read + }; + let Ok(target) = fs::read_link(entry.path()) else { + continue; + }; + let target = target.to_string_lossy(); + if let Some(inode) = target + .strip_prefix("socket:[") + .and_then(|value| value.strip_suffix(']')) + { + if inodes.contains(inode) { + return Ok(true); + } + } + } + Ok(false) +} + +#[cfg(target_os = "linux")] +fn is_geolibre_sidecar_process(pid: i32) -> bool { + let path = format!("/proc/{pid}/cmdline"); + let Ok(command_line) = fs::read(path) else { + return false; + }; + let command_line = String::from_utf8_lossy(&command_line); + command_line.contains("geolibre_server.app.main") + || command_line.contains("geolibre_server/app") +} + +// Recognize OUR Jupyter server (started by start_jupyter_server) by the bundled +// config path on its command line — specific enough not to match the user's own +// jupyter/JupyterHub processes. +#[cfg(target_os = "linux")] +fn is_geolibre_jupyter_process(pid: i32) -> bool { + let path = format!("/proc/{pid}/cmdline"); + let Ok(command_line) = fs::read(path) else { + return false; + }; + let command_line = String::from_utf8_lossy(&command_line); + command_line.contains("jupyter_server_config.py") && command_line.contains("geolibre_server") +} + +#[cfg(unix)] +fn terminate_pid(pid: i32, signal: i32) { + let _ = unsafe { kill(pid, signal) }; +} + +fn sidecar_project_dir(app: &tauri::AppHandle) -> Result { + if let Ok(path) = env::var("GEOLIBRE_SIDECAR_PROJECT_DIR") { + return validate_sidecar_project_dir(PathBuf::from(path)); + } + + if let Ok(resource_dir) = app.path().resource_dir() { + if let Ok(path) = + validate_sidecar_project_dir(resource_dir.join("backend").join("geolibre_server")) + { + return Ok(path); + } + if let Ok(path) = validate_sidecar_project_dir(resource_dir.join("geolibre_server")) { + return Ok(path); + } + } + + validate_sidecar_project_dir( + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("..") + .join("..") + .join("..") + .join("backend") + .join("geolibre_server"), + ) +} + +fn validate_sidecar_project_dir(path: PathBuf) -> Result { + let path = path + .canonicalize() + .map_err(|error| format!("Could not resolve sidecar project path: {error}"))?; + if path.join("pyproject.toml").exists() { + Ok(path) + } else { + Err(format!( + "Could not find GeoLibre sidecar project at {}", + path.display() + )) + } +} + +fn ensure_managed_uv(app: &tauri::AppHandle) -> Result { + if let Ok(path) = env::var("GEOLIBRE_UV") { + let path = PathBuf::from(path); + if path.exists() { + return Ok(path); + } + } + + if let Some(path) = find_executable_on_path(uv_executable_name()) { + return Ok(path); + } + + install_managed_uv(app) +} + +fn install_managed_uv(app: &tauri::AppHandle) -> Result { + let uv_dir = app + .path() + .app_data_dir() + .map_err(|error| format!("Could not resolve app data directory: {error}"))? + .join("runtime") + .join("uv-bin"); + let uv = uv_dir.join(uv_executable_name()); + if uv.exists() { + return Ok(uv); + } + + fs::create_dir_all(&uv_dir) + .map_err(|error| format!("Could not create uv cache directory: {error}"))?; + let script = download_uv_installer(app)?; + let mut command = if cfg!(target_os = "windows") { + let mut command = Command::new("powershell"); + command + .arg("-NoProfile") + .arg("-ExecutionPolicy") + .arg("Bypass") + .arg("-File") + .arg(&script); + command + } else { + let mut command = Command::new("sh"); + command.arg(&script); + command + }; + let output = command + .env("UV_UNMANAGED_INSTALL", &uv_dir) + .output() + .map_err(|error| format!("Could not run uv installer: {error}"))?; + let _ = fs::remove_file(script); + if !output.status.success() { + let detail = String::from_utf8_lossy(if output.stderr.is_empty() { + &output.stdout + } else { + &output.stderr + }); + return Err(format!("uv installer failed: {detail}")); + } + if !uv.exists() { + return Err(format!("uv installer did not create {}", uv.display())); + } + Ok(uv) +} + +fn download_uv_installer(app: &tauri::AppHandle) -> Result { + let url = if cfg!(target_os = "windows") { + format!("{UV_INSTALL_BASE_URL}/install.ps1") + } else { + format!("{UV_INSTALL_BASE_URL}/install.sh") + }; + let response = reqwest::blocking::Client::builder() + .user_agent("GeoLibre Desktop") + .build() + .map_err(|error| format!("Could not create HTTP client: {error}"))? + .get(url) + .send() + .map_err(|error| format!("Could not download uv installer: {error}"))?; + let status = response.status(); + if !status.is_success() { + return Err(format!("uv installer download failed with status {status}")); + } + let extension = if cfg!(target_os = "windows") { + "ps1" + } else { + "sh" + }; + let installer_dir = app + .path() + .app_cache_dir() + .map_err(|error| format!("Could not resolve app cache directory: {error}"))? + .join("installers"); + fs::create_dir_all(&installer_dir) + .map_err(|error| format!("Could not create installer cache directory: {error}"))?; + let script = installer_dir.join(format!("uv-install.{extension}")); + fs::write( + &script, + response + .bytes() + .map_err(|error| format!("Could not read uv installer: {error}"))?, + ) + .map_err(|error| format!("Could not write uv installer: {error}"))?; + Ok(script) +} + +fn uv_executable_name() -> &'static str { + if cfg!(target_os = "windows") { + "uv.exe" + } else { + "uv" + } +} + +fn find_executable_on_path(name: &str) -> Option { + let path = env::var_os("PATH")?; + env::split_paths(&path) + .map(|directory| directory.join(name)) + .find(|candidate| candidate.is_file() && is_executable(candidate)) +} + +#[cfg(unix)] +fn is_executable(path: &std::path::Path) -> bool { + use std::os::unix::fs::PermissionsExt; + path.metadata() + .map(|metadata| metadata.permissions().mode() & 0o111 != 0) + .unwrap_or(false) +} + +#[cfg(not(unix))] +fn is_executable(_path: &std::path::Path) -> bool { + true +} + +fn ensure_martin_binary_path(app: &tauri::AppHandle) -> Result { + let asset_name = martin_asset_name()?; + let executable_name = martin_executable_name(); + let martin_dir = app + .path() + .app_data_dir() + .map_err(|error| format!("Could not resolve app data directory: {error}"))? + .join("martin") + .join(MARTIN_VERSION) + .join( + asset_name + .trim_end_matches(".tar.gz") + .trim_end_matches(".zip"), + ); + let binary_path = martin_dir.join(executable_name); + let temp_binary_path = martin_dir.join(format!("{executable_name}.download")); + + if binary_path.exists() { + return Ok(MartinBinaryInfo { + path: binary_path.to_string_lossy().to_string(), + downloaded: false, + }); + } + + fs::create_dir_all(&martin_dir) + .map_err(|error| format!("Could not create Martin cache directory: {error}"))?; + let _ = fs::remove_file(&temp_binary_path); + let archive = download_martin_asset(asset_name)?; + if let Err(error) = extract_martin_binary(&archive, asset_name, &temp_binary_path) + .and_then(|_| make_executable(&temp_binary_path)) + .and_then(|_| { + fs::rename(&temp_binary_path, &binary_path) + .map_err(|error| format!("Could not install Martin binary: {error}")) + }) + { + let _ = fs::remove_file(&temp_binary_path); + return Err(error); + } + + Ok(MartinBinaryInfo { + path: binary_path.to_string_lossy().to_string(), + downloaded: true, + }) +} + +fn martin_asset_name() -> Result<&'static str, String> { + if cfg!(target_os = "linux") && cfg!(target_arch = "x86_64") { + return Ok("martin-x86_64-unknown-linux-musl.tar.gz"); + } + if cfg!(target_os = "linux") && cfg!(target_arch = "aarch64") { + return Ok("martin-aarch64-unknown-linux-musl.tar.gz"); + } + if cfg!(target_os = "macos") && cfg!(target_arch = "aarch64") { + return Ok("martin-aarch64-apple-darwin.tar.gz"); + } + if cfg!(target_os = "macos") && cfg!(target_arch = "x86_64") { + return Ok("martin-x86_64-apple-darwin.tar.gz"); + } + if cfg!(target_os = "windows") && cfg!(target_arch = "x86_64") { + return Ok("martin-x86_64-pc-windows-msvc.zip"); + } + + Err("No Martin binary release is available for this platform.".to_string()) +} + +fn martin_executable_name() -> &'static str { + if cfg!(target_os = "windows") { + "martin.exe" + } else { + "martin" + } +} + +fn download_martin_asset(asset_name: &str) -> Result, String> { + let url = format!("{MARTIN_RELEASE_BASE_URL}/{MARTIN_VERSION}/{asset_name}"); + let response = reqwest::blocking::Client::builder() + .user_agent("GeoLibre Desktop") + .build() + .map_err(|error| format!("Could not create HTTP client: {error}"))? + .get(url) + .send() + .map_err(|error| format!("Could not download Martin: {error}"))?; + let status = response.status(); + if !status.is_success() { + return Err(format!("Martin download failed with status {status}")); + } + + response + .bytes() + .map(|bytes| bytes.to_vec()) + .map_err(|error| format!("Could not read Martin download: {error}")) +} + +fn extract_martin_binary( + archive: &[u8], + asset_name: &str, + binary_path: &Path, +) -> Result<(), String> { + if asset_name.ends_with(".zip") { + extract_martin_binary_from_zip(archive, binary_path) + } else { + extract_martin_binary_from_tar_gz(archive, binary_path) + } +} + +fn extract_martin_binary_from_tar_gz(archive: &[u8], binary_path: &Path) -> Result<(), String> { + let decoder = GzDecoder::new(Cursor::new(archive)); + let mut archive = tar::Archive::new(decoder); + let executable_name = martin_executable_name(); + let entries = archive + .entries() + .map_err(|error| format!("Could not read Martin archive: {error}"))?; + + for entry in entries { + let mut entry = entry.map_err(|error| format!("Could not read Martin archive: {error}"))?; + let path = entry + .path() + .map_err(|error| format!("Could not read Martin archive path: {error}"))?; + if path.file_name().and_then(|name| name.to_str()) != Some(executable_name) { + continue; + } + + copy_archive_entry_to_path(&mut entry, binary_path)?; + return Ok(()); + } + + Err("Martin archive did not contain the expected executable.".to_string()) +} + +fn extract_martin_binary_from_zip(archive: &[u8], binary_path: &Path) -> Result<(), String> { + let reader = Cursor::new(archive); + let mut archive = zip::ZipArchive::new(reader) + .map_err(|error| format!("Could not read Martin zip: {error}"))?; + let executable_name = martin_executable_name(); + + for index in 0..archive.len() { + let mut file = archive + .by_index(index) + .map_err(|error| format!("Could not read Martin zip entry: {error}"))?; + let path = PathBuf::from(file.name()); + if path.file_name().and_then(|name| name.to_str()) != Some(executable_name) { + continue; + } + + copy_archive_entry_to_path(&mut file, binary_path)?; + return Ok(()); + } + + Err("Martin zip did not contain the expected executable.".to_string()) +} + +fn copy_archive_entry_to_path(reader: &mut R, path: &Path) -> Result<(), String> { + let mut output = + File::create(path).map_err(|error| format!("Could not create Martin binary: {error}"))?; + if let Err(error) = std::io::copy(reader, &mut output) { + let _ = fs::remove_file(path); + return Err(format!("Could not extract Martin binary: {error}")); + } + Ok(()) +} + +#[cfg(unix)] +fn make_executable(path: &Path) -> Result<(), String> { + use std::os::unix::fs::PermissionsExt; + + let mut permissions = fs::metadata(path) + .map_err(|error| format!("Could not read Martin binary permissions: {error}"))? + .permissions(); + permissions.set_mode(0o755); + fs::set_permissions(path, permissions) + .map_err(|error| format!("Could not mark Martin executable: {error}")) +} + +#[cfg(not(unix))] +fn make_executable(_path: &Path) -> Result<(), String> { + Ok(()) +} + +struct SpawnedMartinServer { + base_url: String, + port: u16, + process: MartinProcess, +} + +fn spawn_martin_server( + binary_path: &str, + connection_string: &str, + default_srid: Option<&str>, +) -> Result { + let listener = TcpListener::bind("127.0.0.1:0") + .map_err(|error| format!("Could not reserve a local Martin port: {error}"))?; + let port = listener + .local_addr() + .map(|address| address.port()) + .map_err(|error| format!("Could not read local Martin port: {error}"))?; + let listen_address = format!("127.0.0.1:{port}"); + let base_url = format!("http://127.0.0.1:{port}"); + let mut command = Command::new(binary_path); + command + .arg("-l") + .arg(&listen_address) + .env("DATABASE_URL", connection_string) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()); + + if let Some(default_srid) = default_srid + .map(str::trim) + .filter(|value| !value.is_empty()) + { + command.env("DEFAULT_SRID", default_srid); + } + + drop(listener); + let mut child = command + .spawn() + .map_err(|error| format!("Could not start Martin: {error}"))?; + + if let Err(error) = wait_for_martin_health(&base_url, &mut child) { + let _ = child.kill(); + let _ = child.wait(); + return Err(error); + } + + let _ = child.stdout.take(); + let _ = child.stderr.take(); + + Ok(SpawnedMartinServer { + base_url, + port, + process: MartinProcess { child }, + }) +} + +fn wait_for_martin_health(base_url: &str, child: &mut Child) -> Result<(), String> { + let health_url = format!("{base_url}/health"); + let client = reqwest::blocking::Client::builder() + .timeout(Duration::from_millis(500)) + .build() + .map_err(|error| format!("Could not create HTTP client: {error}"))?; + + for _ in 0..MARTIN_HEALTH_ATTEMPTS { + if let Some(status) = child + .try_wait() + .map_err(|error| format!("Could not inspect Martin process: {error}"))? + { + let output = read_child_output(child); + return Err(if output.trim().is_empty() { + format!("Martin exited before it was ready: {status}") + } else { + format!("Martin exited before it was ready: {output}") + }); + } + + if client + .get(&health_url) + .send() + .map(|response| response.status().is_success()) + .unwrap_or(false) + { + return Ok(()); + } + + thread::sleep(Duration::from_millis(100)); + } + + Err("Martin did not become ready in time.".to_string()) +} + +fn read_child_output(child: &mut Child) -> String { + let mut output = String::new(); + if let Some(mut stdout) = child.stdout.take() { + let _ = stdout.read_to_string(&mut output); + } + if let Some(mut stderr) = child.stderr.take() { + let _ = stderr.read_to_string(&mut output); + } + output +} + +#[derive(Serialize)] +#[serde(rename_all = "camelCase")] +struct MbtilesMetadata { + name: String, + format: String, + tile_type: String, + source_layers: Vec, + min_zoom: Option, + max_zoom: Option, + bounds: Option<[f64; 4]>, + center: Option<[f64; 3]>, + scheme: String, +} + +#[tauri::command] +fn read_mbtiles_metadata(path: String) -> Result { + let connection = open_mbtiles(&path)?; + let metadata = read_metadata_rows(&connection)?; + let fallback_name = Path::new(&path) + .file_stem() + .and_then(|name| name.to_str()) + .unwrap_or("MBTiles Layer") + .to_string(); + let format = metadata + .get("format") + .map(|value| value.to_ascii_lowercase()) + .unwrap_or_else(|| "pbf".to_string()); + let tile_type = match format.as_str() { + "pbf" | "mvt" | "protobuf" => "vector", + _ => "raster", + } + .to_string(); + + Ok(MbtilesMetadata { + name: metadata + .get("name") + .filter(|value| !value.trim().is_empty()) + .cloned() + .unwrap_or(fallback_name), + format, + tile_type, + source_layers: read_vector_source_layers(metadata.get("json")), + min_zoom: metadata + .get("minzoom") + .and_then(|value| value.parse::().ok()), + max_zoom: metadata + .get("maxzoom") + .and_then(|value| value.parse::().ok()), + bounds: metadata.get("bounds").and_then(|value| parse_bounds(value)), + center: metadata.get("center").and_then(|value| parse_center(value)), + scheme: metadata + .get("scheme") + .map(|value| value.to_ascii_lowercase()) + .unwrap_or_else(|| "tms".to_string()), + }) +} + +#[tauri::command] +fn read_mbtiles_tile(path: String, z: u32, x: u32, y: u32) -> Result, String> { + let connection = open_mbtiles(&path)?; + let scheme = read_metadata_value(&connection, "scheme")? + .map(|value| value.to_ascii_lowercase()) + .unwrap_or_else(|| "tms".to_string()); + let tile_row = if scheme == "xyz" { + i64::from(y) + } else { + let row_count = 1_i64 + .checked_shl(z) + .ok_or_else(|| "Tile zoom level is too large".to_string())?; + row_count - 1 - i64::from(y) + }; + if tile_row < 0 { + return Ok(Vec::new()); + } + + let tile_data = connection + .query_row( + "SELECT tile_data FROM tiles WHERE zoom_level = ?1 AND tile_column = ?2 AND tile_row = ?3", + params![i64::from(z), i64::from(x), tile_row], + |row| row.get::<_, Vec>(0), + ) + .optional() + .map_err(|error| format!("Could not read MBTiles tile: {error}"))?; + + Ok(tile_data + .map(decompress_tile_data) + .transpose()? + .unwrap_or_default()) +} + +fn open_mbtiles(path: &str) -> Result { + if !Path::new(path).exists() { + return Err("The selected MBTiles file does not exist".to_string()); + } + + Connection::open_with_flags(path, OpenFlags::SQLITE_OPEN_READ_ONLY) + .map_err(|error| format!("Could not open MBTiles file: {error}")) +} + +fn read_metadata_rows( + connection: &Connection, +) -> Result, String> { + let mut statement = connection + .prepare("SELECT name, value FROM metadata") + .map_err(|error| format!("Could not read MBTiles metadata: {error}"))?; + let rows = statement + .query_map([], |row| { + Ok((row.get::<_, String>(0)?, row.get::<_, String>(1)?)) + }) + .map_err(|error| format!("Could not query MBTiles metadata: {error}"))?; + + let mut metadata = std::collections::HashMap::new(); + for row in rows { + let (name, value) = + row.map_err(|error| format!("Could not parse MBTiles metadata: {error}"))?; + metadata.insert(name.to_ascii_lowercase(), value); + } + Ok(metadata) +} + +fn read_metadata_value(connection: &Connection, name: &str) -> Result, String> { + connection + .query_row( + "SELECT value FROM metadata WHERE lower(name) = lower(?1)", + [name], + |row| row.get::<_, String>(0), + ) + .optional() + .map_err(|error| format!("Could not read MBTiles metadata: {error}")) +} + +fn read_vector_source_layers(json: Option<&String>) -> Vec { + let Some(json) = json else { + return Vec::new(); + }; + let Ok(value) = serde_json::from_str::(json) else { + return Vec::new(); + }; + value + .get("vector_layers") + .and_then(Value::as_array) + .map(|layers| { + layers + .iter() + .filter_map(|layer| layer.get("id").and_then(Value::as_str)) + .map(ToString::to_string) + .collect() + }) + .unwrap_or_default() +} + +fn parse_bounds(value: &str) -> Option<[f64; 4]> { + let values = parse_number_list(value); + if values.len() != 4 { + return None; + } + Some([values[0], values[1], values[2], values[3]]) +} + +fn parse_center(value: &str) -> Option<[f64; 3]> { + let values = parse_number_list(value); + if values.len() < 2 { + return None; + } + Some([values[0], values[1], values.get(2).copied().unwrap_or(0.0)]) +} + +fn parse_number_list(value: &str) -> Vec { + value + .split(',') + .filter_map(|part| part.trim().parse::().ok()) + .collect() +} + +fn decompress_tile_data(data: Vec) -> Result, String> { + if data.starts_with(&[0x1f, 0x8b]) { + let mut decoder = GzDecoder::new(data.as_slice()); + let mut decoded = Vec::new(); + decoder + .read_to_end(&mut decoded) + .map_err(|error| format!("Could not decompress gzip tile: {error}"))?; + return Ok(decoded); + } + + if data.len() > 2 && data[0] == 0x78 { + let mut decoder = ZlibDecoder::new(data.as_slice()); + let mut decoded = Vec::new(); + if decoder.read_to_end(&mut decoded).is_ok() { + return Ok(decoded); + } + } + + Ok(data) +} + +fn create_main_window(app: &mut tauri::App) -> tauri::Result<()> { + let window_config = app + .config() + .app + .windows + .first() + .cloned() + .expect("GeoLibre Desktop requires a main window config"); + + let builder = tauri::WebviewWindowBuilder::from_config(app, &window_config)?; + + // Only desktop opens OAuth flows in child windows; on mobile they navigate + // in-page (or via the system browser), so skip the new-window handler. + #[cfg(desktop)] + let builder = { + let app_handle = app.handle().clone(); + builder.on_new_window(move |url, features| { + create_oauth_popup_window(app_handle.clone(), url, features) + }) + }; + + builder.build()?; + + Ok(()) +} + +#[cfg(desktop)] +fn create_oauth_popup_window( + app_handle: tauri::AppHandle, + url: tauri::Url, + features: tauri::webview::NewWindowFeatures, +) -> tauri::webview::NewWindowResponse { + let popup_id = POPUP_COUNTER.fetch_add(1, Ordering::Relaxed); + let child_app_handle = app_handle.clone(); + let blank_url: tauri::Url = match "about:blank".parse() { + Ok(parsed) => parsed, + Err(error) => { + eprintln!("OAuth popup: could not parse blank URL: {error}"); + return tauri::webview::NewWindowResponse::Deny; + } + }; + let window = match tauri::WebviewWindowBuilder::new( + &app_handle, + format!("oauthPopup{popup_id}"), + tauri::WebviewUrl::External(blank_url), + ) + .window_features(features) + .title(url.as_str()) + .on_new_window(move |url, features| { + create_oauth_popup_window(child_app_handle.clone(), url, features) + }) + .on_document_title_changed(|window, title| { + let _ = window.set_title(&title); + }) + .build() + { + Ok(window) => window, + Err(error) => { + eprintln!("OAuth popup: failed to create popup window: {error}"); + return tauri::webview::NewWindowResponse::Deny; + } + }; + + tauri::webview::NewWindowResponse::Create { window } +} + +#[cfg(target_os = "linux")] +fn configure_linux_webkit() { + // WebKitGTK's DMABUF renderer could fail to allocate GBM buffers on older + // graphics stacks, leaving the Tauri window blank, so it used to be + // disabled here unconditionally. Disabling it also forces a slow readback + // compositing path that visibly drops MapLibre pan/zoom FPS, and the + // allocation bugs are fixed in current WebKitGTK, so keep the workaround + // only for versions older than 2.48. An explicit user/distributor value + // always wins (per WebKit semantics, "0" keeps DMABUF on and any other + // value disables it). Only set the default when unset. + if std::env::var_os("WEBKIT_DISABLE_DMABUF_RENDERER").is_none() { + let webkit_version = unsafe { + ( + webkit2gtk_sys::webkit_get_major_version(), + webkit2gtk_sys::webkit_get_minor_version(), + ) + }; + if webkit_version < (2, 48) { + std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); + } + } + // Prefer portal-backed native dialogs on Linux. This avoids GTK/GIO file + // metadata warnings that can appear around file and folder pickers. + if std::env::var_os("GTK_USE_PORTAL").is_none() { + std::env::set_var("GTK_USE_PORTAL", "1"); + } +} + +#[cfg(not(target_os = "linux"))] +fn configure_linux_webkit() {} + +#[cfg(test)] +mod tests { + use super::{ + ensure_fetchable_url, find_zip_manifest_path, is_allowed_local_vector_path, + is_allowed_project_path, is_disallowed_ip, is_safe_absolute_path, plugin_archive_file_name, + }; + use std::io::{Cursor, Write}; + use std::net::IpAddr; + + #[test] + fn blocks_link_local_and_metadata_ips() { + for ip in [ + "169.254.169.254", // cloud metadata (link-local) + "169.254.0.1", // link-local + "0.0.0.0", // unspecified + "255.255.255.255", // broadcast + "::", // unspecified + "fe80::1", // link-local + "::ffff:169.254.169.254", // IPv4-mapped metadata + "::169.254.169.254", // IPv4-compatible metadata (deprecated) + ] { + assert!( + is_disallowed_ip(ip.parse::().unwrap()), + "expected {ip} to be blocked" + ); + } + } + + #[test] + fn allows_public_loopback_and_lan_ips() { + // Public plus loopback/LAN, which stay reachable for local tile/COG + // data (issue #387). Only link-local/metadata is blocked. + for ip in [ + "8.8.8.8", + "1.1.1.1", + "93.184.216.34", + "2606:4700:4700::1111", + "127.0.0.1", // loopback (local dev tile server) + "192.168.1.10", // LAN + "10.0.0.5", // LAN + "::1", // loopback + ] { + assert!( + !is_disallowed_ip(ip.parse::().unwrap()), + "expected {ip} to be allowed" + ); + } + } + + #[test] + fn ensure_fetchable_url_blocks_metadata_and_bad_schemes() { + assert!(ensure_fetchable_url("http://169.254.169.254/latest/meta-data").is_err()); + assert!(ensure_fetchable_url("file:///etc/passwd").is_err()); + assert!(ensure_fetchable_url("ftp://example.com/x").is_err()); + // Public and loopback/LAN literals are allowed (local data workflow). + assert!(ensure_fetchable_url("https://1.1.1.1/").is_ok()); + assert!(ensure_fetchable_url("http://127.0.0.1:8081/tiles/0/0/0.png").is_ok()); + assert!(ensure_fetchable_url("http://[::1]:8081/data.pmtiles").is_ok()); + } + + #[test] + fn project_path_guard_allows_projects_and_blocks_secrets() { + assert!(is_allowed_project_path("/home/u/map.geolibre.json")); + assert!(is_allowed_project_path("/home/u/map.geolibre")); + assert!(is_allowed_project_path("C:\\Users\\u\\map.geolibre.json")); + // Secrets and traversal are refused. + assert!(!is_allowed_project_path("/home/u/.ssh/id_rsa")); + assert!(!is_allowed_project_path("/home/u/.aws/credentials")); + assert!(!is_allowed_project_path( + "/home/u/../../etc/hosts.geolibre.json" + )); + assert!(!is_allowed_project_path("relative/map.geolibre.json")); + assert!(!is_allowed_project_path( + "\\\\server\\share\\map.geolibre.json" + )); + // A bare .json file (e.g. a JSON credential store) is NOT a project. + assert!(!is_allowed_project_path( + "/home/u/.config/gcloud/application_default_credentials.json" + )); + assert!(!is_allowed_project_path("C:\\Users\\u\\map.json")); + } + + #[test] + fn allows_absolute_vector_paths() { + assert!(is_allowed_local_vector_path("/home/u/cities.geojson")); + assert!(is_allowed_local_vector_path( + "C:\\Users\\smith\\Mile_Markers.geojson" + )); + assert!(is_allowed_local_vector_path("C:/data/roads.gpkg")); + // Case-insensitive extension; a ".." inside the filename is fine. + assert!(is_allowed_local_vector_path("/data/v1..2.SHP")); + } + + #[test] + fn rejects_non_vector_relative_or_traversal_paths() { + // Non-vector extension. + assert!(!is_allowed_local_vector_path("/etc/passwd")); + // Relative path. + assert!(!is_allowed_local_vector_path("cities.geojson")); + // Directory traversal segments. + assert!(!is_allowed_local_vector_path("/data/../etc/secret.geojson")); + assert!(!is_allowed_local_vector_path( + "C:\\data\\..\\secret.geojson" + )); + // UNC network paths, both the backslash and forward-slash forms. + assert!(!is_allowed_local_vector_path( + "\\\\server\\share\\x.geojson" + )); + assert!(!is_allowed_local_vector_path("//server/share/x.geojson")); + // Empty string is not an absolute path. + assert!(!is_allowed_local_vector_path("")); + // No extension, and a trailing dot (empty extension). + assert!(!is_allowed_local_vector_path("/home/user/noextension")); + assert!(!is_allowed_local_vector_path("/home/user/file.")); + } + + fn zip_with_names(names: &[&str]) -> Vec { + let mut writer = zip::ZipWriter::new(Cursor::new(Vec::new())); + let options = zip::write::SimpleFileOptions::default() + .compression_method(zip::CompressionMethod::Stored); + for name in names { + writer.start_file(*name, options).unwrap(); + writer.write_all(b"x").unwrap(); + } + writer.finish().unwrap().into_inner() + } + + fn manifest_path(names: &[&str]) -> Option { + let bytes = zip_with_names(names); + let archive = zip::ZipArchive::new(Cursor::new(bytes)).unwrap(); + find_zip_manifest_path(&archive) + } + + #[test] + fn finds_root_manifest() { + assert_eq!( + manifest_path(&["plugin.json", "dist/plugin.js"]).as_deref(), + Some("plugin.json") + ); + } + + #[test] + fn finds_manifest_inside_a_wrapping_folder() { + assert_eq!( + manifest_path(&["my-plugin/plugin.json", "my-plugin/dist/plugin.js"]).as_deref(), + Some("my-plugin/plugin.json") + ); + } + + #[test] + fn prefers_root_and_ignores_macosx_metadata() { + // A root manifest wins over a deeper one. + assert_eq!( + manifest_path(&["wrap/plugin.json", "plugin.json"]).as_deref(), + Some("plugin.json") + ); + // The __MACOSX folder is skipped, so the real wrapped manifest is found. + assert_eq!( + manifest_path(&["__MACOSX/wrap/._plugin.json", "wrap/plugin.json"]).as_deref(), + Some("wrap/plugin.json") + ); + } + + #[test] + fn returns_none_without_a_manifest() { + assert_eq!(manifest_path(&["dist/plugin.js"]), None); + } + + #[test] + fn keeps_safe_plugin_ids() { + assert_eq!(plugin_archive_file_name("maplibre-foo"), "maplibre-foo.zip"); + assert_eq!(plugin_archive_file_name("foo.bar_2"), "foo.bar_2.zip"); + } + + #[test] + fn sanitizes_unsafe_characters_and_traversal() { + // Path separators and other characters cannot escape the plugins dir; + // leading dots are then stripped, so "../evil" collapses to "_evil". + assert_eq!(plugin_archive_file_name("../evil"), "_evil.zip"); + assert_eq!(plugin_archive_file_name("a/b"), "a_b.zip"); + // Leading dots are stripped so the archive is never hidden. + assert_eq!(plugin_archive_file_name("..hidden"), "hidden.zip"); + // A name that sanitizes away entirely falls back to a fixed stem. + assert_eq!(plugin_archive_file_name("..."), "plugin.zip"); + } + + #[test] + fn is_safe_absolute_path_accepts_absolute_local_dirs() { + assert!(is_safe_absolute_path("/home/user/data")); + assert!(is_safe_absolute_path("/data")); + assert!(is_safe_absolute_path("C:\\Users\\me\\gis")); + assert!(is_safe_absolute_path("C:/Users/me/gis")); + // A ".." inside a name (not a traversal segment) is fine. + assert!(is_safe_absolute_path("/home/user/v1..2")); + } + + #[test] + fn is_safe_absolute_path_rejects_unc_traversal_and_relative() { + // UNC shares (both forms) can auto-authenticate against a remote host. + assert!(!is_safe_absolute_path("\\\\server\\share")); + assert!(!is_safe_absolute_path("//server/share")); + // `..` traversal segments. + assert!(!is_safe_absolute_path("/home/user/../etc")); + assert!(!is_safe_absolute_path("C:\\a\\..\\b")); + // Relative / non-absolute and empty input. + assert!(!is_safe_absolute_path("home/user")); + assert!(!is_safe_absolute_path("./data")); + assert!(!is_safe_absolute_path("")); + assert!(!is_safe_absolute_path("C:")); // drive letter without a separator + } +} diff --git a/apps/geolibre-desktop/src-tauri/src/main.rs b/apps/geolibre-desktop/src-tauri/src/main.rs new file mode 100644 index 0000000..8ee74db --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/src/main.rs @@ -0,0 +1,6 @@ +// Prevents additional console window on Windows in release +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + geolibre_desktop_lib::run() +} diff --git a/apps/geolibre-desktop/src-tauri/src/native_duckdb.rs b/apps/geolibre-desktop/src-tauri/src/native_duckdb.rs new file mode 100644 index 0000000..47cdde9 --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/src/native_duckdb.rs @@ -0,0 +1,1076 @@ +use chrono::{Duration, NaiveDate, NaiveTime, SecondsFormat, TimeZone, Utc}; +use duckdb::{ + types::{TimeUnit, Value, ValueRef}, + Connection, Row, +}; +use serde_json::{json, Map}; +use std::path::Path; + +const GEOMETRY_JSON_COLUMN: &str = "__geolibre_geometry_geojson"; +const FEATURE_COUNT_COLUMN: &str = "__geolibre_feature_count"; +const TARGET_CRS: &str = "EPSG:4326"; +const WKB_GEOMETRY_COLUMN_NAMES: [&str; 6] = [ + "geometry", + "geom", + "wkb_geometry", + "geometry_wkb", + "geom_wkb", + "wkb", +]; + +#[derive(Clone, Debug)] +struct NativeVectorOptions { + path: String, + extension: String, + layer: Option, + override_source_crs: Option, +} + +#[derive(Debug)] +struct DetectedGeometry { + column: String, + is_wkb: bool, + is_base64_wkb: bool, + requires_base64_wkb_validation: bool, + base64_wkb_candidates: Vec, +} + +#[derive(Debug)] +struct DescribedColumn { + name: String, + column_type: String, +} + +#[tauri::command] +pub async fn count_native_vector_file_features( + path: String, + layer: Option, +) -> Result { + let options = native_options(path, layer, None)?; + tauri::async_runtime::spawn_blocking(move || { + count_native_vector_file_features_blocking(options) + }) + .await + .map_err(|error| format!("Native DuckDB count task failed: {error}"))? +} + +#[tauri::command] +pub async fn load_native_vector_file( + path: String, + layer: Option, + override_source_crs: Option, +) -> Result { + let options = native_options(path, layer, override_source_crs)?; + tauri::async_runtime::spawn_blocking(move || load_native_vector_file_blocking(options)) + .await + .map_err(|error| format!("Native DuckDB load task failed: {error}"))? +} + +fn native_options( + path: String, + layer: Option, + override_source_crs: Option, +) -> Result { + if !crate::is_allowed_local_vector_path(&path) { + return Err(format!( + "Refusing to read \"{path}\": not an absolute local vector file path" + )); + } + if has_duckdb_glob_metacharacter(&path) { + return Err(format!( + "Refusing to read \"{path}\": glob paths are not allowed" + )); + } + Ok(NativeVectorOptions { + extension: vector_extension(&path), + path, + layer: blank_to_none(layer), + override_source_crs: blank_to_none(override_source_crs), + }) +} + +fn blank_to_none(value: Option) -> Option { + value.and_then(|value| { + let trimmed = value.trim(); + if trimmed.is_empty() { + None + } else { + Some(trimmed.to_string()) + } + }) +} + +fn vector_extension(path: &str) -> String { + let name = Path::new(path) + .file_name() + .and_then(|name| name.to_str()) + .unwrap_or(path) + .to_ascii_lowercase(); + if name.ends_with(".geoparquet") { + "geoparquet".to_string() + } else { + Path::new(&name) + .extension() + .and_then(|extension| extension.to_str()) + .unwrap_or("") + .to_string() + } +} + +fn has_duckdb_glob_metacharacter(path: &str) -> bool { + path.contains('*') + || path.contains('?') + || (has_duckdb_bracket_glob(path) && !Path::new(path).is_file()) +} + +fn has_duckdb_bracket_glob(path: &str) -> bool { + let bytes = path.as_bytes(); + for (index, byte) in bytes.iter().enumerate() { + if *byte != b'[' { + continue; + } + if bytes[index + 1..] + .iter() + .position(|candidate| *candidate == b']') + .is_some_and(|closing_index| closing_index > 0) + { + return true; + } + } + false +} + +fn count_native_vector_file_features_blocking( + options: NativeVectorOptions, +) -> Result { + let conn = open_native_duckdb()?; + let sql = source_sql(&options); + let count_sql = format!( + "SELECT count(*) AS {} FROM ({sql}) AS data", + quote_identifier(FEATURE_COUNT_COLUMN) + ); + conn.query_row(&count_sql, [], |row| row.get::<_, i64>(0)) + .map(|count| count.max(0) as usize) + .map_err(|error| format!("Could not count vector features with native DuckDB: {error}")) +} + +fn load_native_vector_file_blocking( + options: NativeVectorOptions, +) -> Result { + let conn = open_native_duckdb()?; + let sql = source_sql(&options); + let columns = describe_source_columns(&conn, &sql)?; + let detected = detect_geometry_column(&conn, &sql, &columns)?; + let property_columns: Vec = columns + .iter() + .filter(|column| column.name != detected.column) + .map(|column| column.name.clone()) + .collect(); + let source_crs = match options.override_source_crs.clone() { + Some(crs) => Some(crs), + None => read_source_crs(&conn, &options), + }; + let geometry_json_sql = geometry_geojson_sql(&geometry_expr(&detected), source_crs.as_deref()); + let mut select_columns: Vec = property_columns + .iter() + .map(|column| quote_identifier(column)) + .collect(); + select_columns.push(format!( + "{geometry_json_sql} AS {}", + quote_identifier(GEOMETRY_JSON_COLUMN) + )); + let load_sql = format!("SELECT {} FROM ({sql}) AS data", select_columns.join(", ")); + + let mut stmt = conn + .prepare(&load_sql) + .map_err(|error| format!("Could not prepare native DuckDB vector query: {error}"))?; + let mut column_names = property_columns; + column_names.push(GEOMETRY_JSON_COLUMN.to_string()); + let mut rows = stmt + .query([]) + .map_err(|error| format!("Could not read vector rows with native DuckDB: {error}"))?; + let mut features = Vec::new(); + while let Some(row) = rows + .next() + .map_err(|error| format!("Could not read vector row with native DuckDB: {error}"))? + { + features.push(row_to_feature(row, &column_names)?); + } + Ok(json!({ + "type": "FeatureCollection", + "features": features, + })) +} + +fn open_native_duckdb() -> Result { + let conn = Connection::open_in_memory() + .map_err(|error| format!("Could not open native DuckDB: {error}"))?; + ensure_spatial_extension(&conn)?; + Ok(conn) +} + +fn ensure_spatial_extension(conn: &Connection) -> Result<(), String> { + if let Some(path) = trusted_spatial_extension_path()? { + conn.execute_batch(&format!( + "LOAD {}", + quote_sql_string(&path.replace('\\', "/")) + )) + .map_err(|error| { + format!("Could not load DuckDB spatial extension from \"{path}\": {error}") + })?; + return Ok(()); + } + + conn.execute_batch("LOAD spatial;") + .map_err(|error| format!("Could not load DuckDB spatial extension: {error}")) +} + +fn trusted_spatial_extension_path() -> Result, String> { + let Some(path) = blank_to_none(std::env::var("GEOLIBRE_DUCKDB_SPATIAL_EXTENSION_PATH").ok()) + else { + return Ok(None); + }; + let canonical = Path::new(&path) + .canonicalize() + .map_err(|error| format!("Could not resolve DuckDB spatial extension path: {error}"))?; + if !canonical.is_file() { + return Err(format!( + "DuckDB spatial extension path is not a file: {}", + canonical.display() + )); + } + canonical + .to_str() + .map(|path| Some(path.to_string())) + .ok_or_else(|| "DuckDB spatial extension path was not valid UTF-8".to_string()) +} + +fn is_parquet_extension(extension: &str) -> bool { + extension == "parquet" || extension == "geoparquet" +} + +fn source_sql(options: &NativeVectorOptions) -> String { + let quoted_path = quote_sql_string(&options.path.replace('\\', "/")); + if is_parquet_extension(&options.extension) { + return format!("SELECT * FROM read_parquet({quoted_path})"); + } + let layer_arg = options + .layer + .as_ref() + .map(|layer| format!(", layer={}", quote_sql_string(layer))) + .unwrap_or_default(); + format!("SELECT * FROM ST_Read({quoted_path}{layer_arg})") +} + +fn describe_source_columns(conn: &Connection, sql: &str) -> Result, String> { + let describe_sql = format!("DESCRIBE {sql}"); + let mut stmt = conn + .prepare(&describe_sql) + .map_err(|error| format!("Could not describe vector source with native DuckDB: {error}"))?; + let mut rows = stmt + .query([]) + .map_err(|error| format!("Could not inspect vector columns with native DuckDB: {error}"))?; + let mut columns = Vec::new(); + while let Some(row) = rows + .next() + .map_err(|error| format!("Could not read vector column description: {error}"))? + { + let column_name: String = row + .get(0) + .map_err(|error| format!("Could not read described column name: {error}"))?; + let column_type: String = row + .get(1) + .map_err(|error| format!("Could not read described column type: {error}"))?; + columns.push(DescribedColumn { + name: column_name, + column_type, + }); + } + Ok(columns) +} + +fn detect_geometry_column( + conn: &Connection, + source_sql: &str, + columns: &[DescribedColumn], +) -> Result { + let detected = detect_geometry_column_from_schema(columns)?; + if !detected.requires_base64_wkb_validation { + return Ok(detected); + } + for column in &detected.base64_wkb_candidates { + if has_valid_base64_wkb_values(conn, source_sql, column)? { + return Ok(DetectedGeometry { + column: column.clone(), + is_wkb: detected.is_wkb, + is_base64_wkb: detected.is_base64_wkb, + requires_base64_wkb_validation: false, + base64_wkb_candidates: Vec::new(), + }); + } + } + Err("DuckDB did not find a geometry column in this file.".to_string()) +} + +fn detect_geometry_column_from_schema( + columns: &[DescribedColumn], +) -> Result { + let mut wkb_candidate: Option<(usize, String)> = None; + let mut base64_wkb_candidates: Vec<(usize, String)> = Vec::new(); + for column in columns { + if column + .column_type + .to_ascii_uppercase() + .starts_with("GEOMETRY") + { + return Ok(DetectedGeometry { + column: column.name.clone(), + is_wkb: false, + is_base64_wkb: false, + requires_base64_wkb_validation: false, + base64_wkb_candidates: Vec::new(), + }); + } + let lower_name = column.name.to_ascii_lowercase(); + let upper_type = column.column_type.to_ascii_uppercase(); + if !WKB_GEOMETRY_COLUMN_NAMES.contains(&lower_name.as_str()) { + continue; + } + let rank = WKB_GEOMETRY_COLUMN_NAMES + .iter() + .position(|candidate| *candidate == lower_name.as_str()) + .unwrap_or(WKB_GEOMETRY_COLUMN_NAMES.len()); + if upper_type.starts_with("BLOB") + || upper_type.starts_with("BINARY") + || upper_type.starts_with("VARBINARY") + { + if wkb_candidate + .as_ref() + .map(|(current_rank, _)| rank < *current_rank) + .unwrap_or(true) + { + wkb_candidate = Some((rank, column.name.clone())); + } + } else if upper_type.starts_with("VARCHAR") + || upper_type.starts_with("TEXT") + || upper_type.starts_with("STRING") + { + base64_wkb_candidates.push((rank, column.name.clone())); + } + } + + if let Some((_, column)) = wkb_candidate { + return Ok(DetectedGeometry { + column, + is_wkb: true, + is_base64_wkb: false, + requires_base64_wkb_validation: false, + base64_wkb_candidates: Vec::new(), + }); + } + base64_wkb_candidates.sort_by_key(|(rank, _)| *rank); + if let Some((_, column)) = base64_wkb_candidates.first() { + return Ok(DetectedGeometry { + column: column.clone(), + is_wkb: true, + is_base64_wkb: true, + requires_base64_wkb_validation: true, + base64_wkb_candidates: base64_wkb_candidates + .into_iter() + .map(|(_, column)| column) + .collect(), + }); + } + + Err("DuckDB did not find a geometry column in this file.".to_string()) +} + +fn has_valid_base64_wkb_values( + conn: &Connection, + source_sql: &str, + column: &str, +) -> Result { + let column_sql = quote_identifier(column); + let sample_column = quote_identifier("__geolibre_base64_wkb_sample"); + let probe_sql = format!( + "SELECT count(*) AS sample_count, \ + count(TRY(ST_GeomFromWKB(from_base64({sample_column})))) AS valid_count \ + FROM (SELECT {column_sql} AS {sample_column} FROM ({source_sql}) AS data \ + WHERE {column_sql} IS NOT NULL LIMIT 20) AS sample" + ); + let (sample_count, valid_count): (i64, i64) = conn + .query_row(&probe_sql, [], |row| Ok((row.get(0)?, row.get(1)?))) + .map_err(|error| format!("Could not validate base64 WKB geometry values: {error}"))?; + // Require every sampled non-null value to decode as WKB so a user attribute + // named "geometry" or "wkb" is not promoted based on a partial match. + Ok(sample_count > 0 && sample_count == valid_count) +} + +fn read_source_crs(conn: &Connection, options: &NativeVectorOptions) -> Option { + if is_parquet_extension(&options.extension) { + return read_geoparquet_source_crs(conn, options); + } + let meta_sql = format!( + "SELECT layers[1].geometry_fields[1].crs.auth_name AS auth_name, \ + layers[1].geometry_fields[1].crs.auth_code AS auth_code \ + FROM ST_Read_Meta({})", + quote_sql_string(&options.path.replace('\\', "/")) + ); + let auth_crs = conn + .query_row(&meta_sql, [], |row| { + let auth_name: Option = row.get(0)?; + let auth_code: Option = row.get(1)?; + Ok((auth_name, auth_code)) + }) + .ok() + .and_then(|(auth_name, auth_code)| { + let auth_name = auth_name?.trim().to_ascii_uppercase(); + let auth_code = auth_code?.trim().to_string(); + if auth_name.is_empty() || auth_code.is_empty() { + None + } else { + Some(format!("{auth_name}:{auth_code}")) + } + }); + if auth_crs.is_some() { + return auth_crs; + } + // ST_Read_Meta resolved no EPSG authority code (e.g. a custom ESRI `.prj` + // without an AUTHORITY tag). Fall back to the shapefile's `.prj` sidecar + // WKT, which ST_Transform accepts, mirroring the DuckDB-WASM loader so a + // projected shapefile still reprojects instead of loading in source + // coordinates (issue #1148). + if options.extension == "shp" { + return read_prj_sidecar_crs(&options.path); + } + None +} + +/// The WKT text of a shapefile's `.prj` sidecar, or `None` when it is absent or +/// empty. Used as the CRS fallback when `ST_Read_Meta` reports no authority code. +fn read_prj_sidecar_crs(shp_path: &str) -> Option { + let path = Path::new(shp_path); + // Fast path: the sidecar usually shares the `.shp`'s exact base name with a + // `.prj` or `.PRJ` extension. + for extension in ["prj", "PRJ"] { + if let Some(crs) = read_nonempty_trimmed(&path.with_extension(extension)) { + return Some(crs); + } + } + // Fallback for mixed-case naming (e.g. `Foo.Prj`, or a `Foo.shp` whose + // sidecar is `foo.PRJ`) on a case-sensitive filesystem: scan the directory + // for a file whose stem and `prj` extension both match case-insensitively. + let stem = path.file_stem()?.to_str()?; + let dir = path.parent()?; + for entry in std::fs::read_dir(dir).ok()?.flatten() { + let entry_path = entry.path(); + let stem_matches = entry_path + .file_stem() + .and_then(|entry_stem| entry_stem.to_str()) + .is_some_and(|entry_stem| entry_stem.eq_ignore_ascii_case(stem)); + let is_prj = entry_path + .extension() + .and_then(|extension| extension.to_str()) + .is_some_and(|extension| extension.eq_ignore_ascii_case("prj")); + if stem_matches && is_prj { + if let Some(crs) = read_nonempty_trimmed(&entry_path) { + return Some(crs); + } + } + } + None +} + +/// The trimmed contents of a file, or `None` when it cannot be read or is empty. +fn read_nonempty_trimmed(path: &Path) -> Option { + // Decode lossily rather than with `read_to_string` so a `.prj` carrying a + // stray non-UTF-8 byte still yields its WKT instead of silently reverting to + // the pre-fix "no reprojection" behavior. + let bytes = std::fs::read(path).ok()?; + let trimmed = String::from_utf8_lossy(&bytes).trim().to_string(); + if trimmed.is_empty() { + None + } else { + Some(trimmed) + } +} + +fn read_geoparquet_source_crs(conn: &Connection, options: &NativeVectorOptions) -> Option { + let metadata_sql = format!( + "SELECT decode(value) FROM parquet_kv_metadata({}) WHERE decode(key) = 'geo' LIMIT 1", + quote_sql_string(&options.path.replace('\\', "/")) + ); + let metadata: String = conn.query_row(&metadata_sql, [], |row| row.get(0)).ok()?; + geoparquet_crs_from_metadata(&metadata) +} + +fn geoparquet_crs_from_metadata(metadata: &str) -> Option { + let value: serde_json::Value = serde_json::from_str(metadata).ok()?; + let primary_column = value + .get("primary_column") + .and_then(|value| value.as_str()) + .unwrap_or("geometry"); + let crs = value.get("columns")?.get(primary_column)?.get("crs")?; + if crs.is_null() { + return None; + } + crs_auth_code(crs) +} + +fn crs_auth_code(crs: &serde_json::Value) -> Option { + let id = match crs.get("id")? { + serde_json::Value::Array(ids) => ids.last()?, + id => id, + }; + let authority = id + .get("authority") + .or_else(|| id.get("auth_name"))? + .as_str()? + .trim() + .to_ascii_uppercase(); + let code = id.get("code").or_else(|| id.get("auth_code"))?; + let code = match code { + serde_json::Value::String(value) => value.trim().to_string(), + serde_json::Value::Number(value) => value.to_string(), + _ => return None, + }; + if authority.is_empty() || code.is_empty() { + None + } else { + Some(format!("{authority}:{code}")) + } +} + +fn geometry_expr(detected: &DetectedGeometry) -> String { + assert!( + !detected.requires_base64_wkb_validation, + "base64 WKB geometry must be value-validated before SQL generation" + ); + let column = quote_identifier(&detected.column); + if detected.is_wkb { + let wkb = if detected.is_base64_wkb { + format!("from_base64({column})") + } else { + column + }; + format!("ST_GeomFromWKB({wkb})") + } else { + column + } +} + +fn geometry_geojson_sql(geometry_expression: &str, source_crs: Option<&str>) -> String { + match source_crs { + Some(source_crs) => format!( + "ST_AsGeoJSON(ST_Transform({geometry_expression}, {}, {}, true))", + quote_sql_string(source_crs), + quote_sql_string(TARGET_CRS) + ), + None => format!("ST_AsGeoJSON({geometry_expression})"), + } +} + +fn row_to_feature(row: &Row<'_>, column_names: &[String]) -> Result { + let mut properties = Map::new(); + let mut geometry = serde_json::Value::Null; + + for (index, column_name) in column_names.iter().enumerate() { + let value = row.get_ref(index).map_err(|error| { + format!("Could not read native DuckDB column \"{column_name}\": {error}") + })?; + if column_name == GEOMETRY_JSON_COLUMN { + geometry = match value { + ValueRef::Null => serde_json::Value::Null, + ValueRef::Text(bytes) => { + let text = std::str::from_utf8(bytes) + .map_err(|error| format!("Geometry GeoJSON was not UTF-8: {error}"))?; + serde_json::from_str(text) + .map_err(|error| format!("Geometry GeoJSON was invalid: {error}"))? + } + _ => serde_json::Value::Null, + }; + continue; + } + if matches!(value, ValueRef::Blob(_)) { + continue; + } + properties.insert(column_name.clone(), duckdb_value_to_json(&value.to_owned())); + } + + Ok(json!({ + "type": "Feature", + "geometry": geometry, + "properties": properties, + })) +} + +fn duckdb_value_to_json(value: &Value) -> serde_json::Value { + match value { + Value::Null => serde_json::Value::Null, + Value::Boolean(value) => json!(value), + Value::TinyInt(value) => json_number_or_string_i128(*value as i128), + Value::SmallInt(value) => json_number_or_string_i128(*value as i128), + Value::Int(value) => json_number_or_string_i128(*value as i128), + Value::BigInt(value) => json_number_or_string_i128(*value as i128), + Value::HugeInt(value) => json_number_or_string_i128(*value), + Value::UTinyInt(value) => json_number_or_string_u128(*value as u128), + Value::USmallInt(value) => json_number_or_string_u128(*value as u128), + Value::UInt(value) => json_number_or_string_u128(*value as u128), + Value::UBigInt(value) => json_number_or_string_u128(*value as u128), + Value::Float(value) => json!(value), + Value::Double(value) => json!(value), + Value::Decimal(value) => json!(value.to_string()), + Value::Timestamp(unit, value) => json!(format_timestamp(*unit, *value)), + Value::Text(value) => json!(value), + Value::Blob(_) => serde_json::Value::Null, + Value::Date32(value) => json!(format_date32(*value)), + Value::Time64(unit, value) => json!(format_time(*unit, *value)), + Value::Interval { + months, + days, + nanos, + } => json!(format!("{months} months {days} days {nanos} ns")), + Value::List(values) | Value::Array(values) => { + serde_json::Value::Array(values.iter().map(duckdb_value_to_json).collect()) + } + Value::Enum(value) => json!(value), + Value::Struct(values) => { + let mut object = Map::new(); + for (key, value) in values.iter() { + object.insert(key.clone(), duckdb_value_to_json(value)); + } + serde_json::Value::Object(object) + } + Value::Map(values) => { + let mut object = Map::new(); + for (key, value) in values.iter() { + object.insert(value_json_key(key), duckdb_value_to_json(value)); + } + serde_json::Value::Object(object) + } + Value::Union(value) => duckdb_value_to_json(value), + } +} + +fn json_number_or_string_i128(value: i128) -> serde_json::Value { + const MIN_SAFE_INTEGER: i128 = -9_007_199_254_740_991; + const MAX_SAFE_INTEGER: i128 = 9_007_199_254_740_991; + if (MIN_SAFE_INTEGER..=MAX_SAFE_INTEGER).contains(&value) { + json!(value as i64) + } else { + json!(value.to_string()) + } +} + +fn json_number_or_string_u128(value: u128) -> serde_json::Value { + const MAX_SAFE_INTEGER: u128 = 9_007_199_254_740_991; + if value <= MAX_SAFE_INTEGER { + json!(value as u64) + } else { + json!(value.to_string()) + } +} + +fn format_timestamp(unit: TimeUnit, value: i64) -> String { + let micros = match unit { + TimeUnit::Second => value.saturating_mul(1_000_000), + TimeUnit::Millisecond => value.saturating_mul(1_000), + TimeUnit::Microsecond => value, + TimeUnit::Nanosecond => value / 1_000, + }; + let seconds = micros.div_euclid(1_000_000); + let nanos = (micros.rem_euclid(1_000_000) as u32).saturating_mul(1_000); + Utc.timestamp_opt(seconds, nanos) + .single() + .map(|datetime| datetime.to_rfc3339_opts(SecondsFormat::Micros, true)) + .unwrap_or_else(|| format!("{micros}us since 1970-01-01T00:00:00Z")) +} + +fn format_date32(value: i32) -> String { + NaiveDate::from_ymd_opt(1970, 1, 1) + .and_then(|epoch| epoch.checked_add_signed(Duration::days(value as i64))) + .map(|date| date.to_string()) + .unwrap_or_else(|| value.to_string()) +} + +fn format_time(unit: TimeUnit, value: i64) -> String { + let micros = match unit { + TimeUnit::Second => value.saturating_mul(1_000_000), + TimeUnit::Millisecond => value.saturating_mul(1_000), + TimeUnit::Microsecond => value, + TimeUnit::Nanosecond => value / 1_000, + }; + let seconds = micros.div_euclid(1_000_000); + let nanos = (micros.rem_euclid(1_000_000) as u32).saturating_mul(1_000); + NaiveTime::from_num_seconds_from_midnight_opt(u32::try_from(seconds).unwrap_or(u32::MAX), nanos) + .map(|time| time.format("%H:%M:%S%.6f").to_string()) + .unwrap_or_else(|| format!("{micros}us")) +} + +fn value_json_key(value: &Value) -> String { + match duckdb_value_to_json(value) { + serde_json::Value::String(value) => value, + other => other.to_string(), + } +} + +fn quote_sql_string(value: &str) -> String { + format!("'{}'", value.replace('\'', "''")) +} + +fn quote_identifier(value: &str) -> String { + format!("\"{}\"", value.replace('"', "\"\"")) +} + +#[cfg(test)] +mod tests { + use super::*; + use std::time::{SystemTime, UNIX_EPOCH}; + + fn temp_geoparquet_path() -> String { + let suffix = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time before Unix epoch") + .as_nanos(); + std::env::temp_dir() + .join(format!( + "geolibre-native-duckdb-{suffix}-{}.geoparquet", + std::process::id() + )) + .to_string_lossy() + .to_string() + } + + fn create_real_geoparquet(path: &str) { + let conn = Connection::open_in_memory().expect("open DuckDB"); + install_spatial_extension_for_tests(&conn).expect("load spatial"); + conn.execute_batch(&format!( + " + CREATE TABLE places AS + SELECT 1 AS id, 'San Francisco' AS name, ST_Point(-122.4194, 37.7749) AS geometry + UNION ALL + SELECT 2 AS id, 'New York' AS name, ST_Point(-73.9857, 40.7484) AS geometry; + COPY places TO {} (FORMAT PARQUET); + ", + quote_sql_string(path) + )) + .expect("write GeoParquet fixture"); + } + + fn create_base64_wkb_parquet(path: &str) { + let conn = Connection::open_in_memory().expect("open DuckDB"); + conn.execute_batch(&format!( + " + CREATE TABLE places AS + SELECT + 1 AS id, + 'Luxembourg' AS name, + 'AQMAAAABAAAABQAAAFCW6nIMPRdA3PVYDQjGSECxTV13Nj0XQHRYwfoLxkhA4MiF5+M8F0BWd0PCEcZIQPjdWB27PBdAIePTAA7GSEBQlupyDD0XQNz1WA0IxkhA' AS geometry; + COPY places TO {} (FORMAT PARQUET); + ", + quote_sql_string(path) + )) + .expect("write base64 WKB Parquet fixture"); + } + + fn create_plain_string_geometry_parquet(path: &str) { + let conn = Connection::open_in_memory().expect("open DuckDB"); + conn.execute_batch(&format!( + " + CREATE TABLE places AS + SELECT 1 AS id, 'not WKB' AS geometry; + COPY places TO {} (FORMAT PARQUET); + ", + quote_sql_string(path) + )) + .expect("write plain string geometry fixture"); + } + + fn create_ranked_base64_wkb_candidates_parquet(path: &str) { + let conn = Connection::open_in_memory().expect("open DuckDB"); + conn.execute_batch(&format!( + " + CREATE TABLE places AS + SELECT + 1 AS id, + 'not WKB' AS geometry, + 'AQMAAAABAAAABQAAAFCW6nIMPRdA3PVYDQjGSECxTV13Nj0XQHRYwfoLxkhA4MiF5+M8F0BWd0PCEcZIQPjdWB27PBdAIePTAA7GSEBQlupyDD0XQNz1WA0IxkhA' AS wkb; + COPY places TO {} (FORMAT PARQUET); + ", + quote_sql_string(path) + )) + .expect("write ranked base64 WKB candidate fixture"); + } + + fn install_spatial_extension_for_tests(conn: &Connection) -> Result<(), String> { + conn.execute_batch("INSTALL spatial; LOAD spatial;") + .map_err(|error| format!("Could not install/load DuckDB spatial extension: {error}")) + } + + #[test] + fn native_loader_reads_real_geoparquet_as_geojson() { + let path = temp_geoparquet_path(); + create_real_geoparquet(&path); + + let options = native_options(path.clone(), None, None).expect("native options"); + let feature_count = + count_native_vector_file_features_blocking(options.clone()).expect("count features"); + assert_eq!(feature_count, 2); + + let collection = load_native_vector_file_blocking(options).expect("load vector file"); + assert_eq!(collection["type"], "FeatureCollection"); + let features = collection["features"].as_array().expect("features array"); + assert_eq!(features.len(), 2); + assert_eq!(features[0]["properties"]["id"], 1); + assert_eq!(features[0]["properties"]["name"], "San Francisco"); + assert_eq!(features[0]["geometry"]["type"], "Point"); + assert_eq!(features[0]["geometry"]["coordinates"][0], -122.4194); + assert_eq!(features[0]["geometry"]["coordinates"][1], 37.7749); + + let _ = std::fs::remove_file(path); + } + + #[test] + fn native_loader_reads_base64_wkb_geometry_column() { + let path = temp_geoparquet_path(); + create_base64_wkb_parquet(&path); + + let options = native_options(path.clone(), None, None).expect("native options"); + let collection = load_native_vector_file_blocking(options).expect("load vector file"); + let features = collection["features"].as_array().expect("features array"); + assert_eq!(features.len(), 1); + assert_eq!(features[0]["properties"]["id"], 1); + assert_eq!(features[0]["properties"]["name"], "Luxembourg"); + assert_eq!(features[0]["geometry"]["type"], "Polygon"); + assert_eq!( + features[0]["geometry"]["coordinates"][0][0][0], + 5.809617801254333 + ); + assert_eq!( + features[0]["geometry"]["coordinates"][0][0][1], + 49.54712073177117 + ); + + let _ = std::fs::remove_file(path); + } + + #[test] + fn native_loader_rejects_plain_string_geometry_column() { + let path = temp_geoparquet_path(); + create_plain_string_geometry_parquet(&path); + + let options = native_options(path.clone(), None, None).expect("native options"); + let error = load_native_vector_file_blocking(options).expect_err("reject plain string"); + assert!(error.contains("DuckDB did not find a geometry column")); + + let _ = std::fs::remove_file(path); + } + + #[test] + fn native_loader_tries_ranked_base64_wkb_candidates() { + let path = temp_geoparquet_path(); + create_ranked_base64_wkb_candidates_parquet(&path); + + let options = native_options(path.clone(), None, None).expect("native options"); + let collection = load_native_vector_file_blocking(options).expect("load vector file"); + let features = collection["features"].as_array().expect("features array"); + assert_eq!(features.len(), 1); + assert_eq!(features[0]["properties"]["id"], 1); + assert_eq!(features[0]["properties"]["geometry"], "not WKB"); + assert_eq!(features[0]["geometry"]["type"], "Polygon"); + assert_eq!( + features[0]["geometry"]["coordinates"][0][0][0], + 5.809617801254333 + ); + + let _ = std::fs::remove_file(path); + } + + #[test] + fn native_options_rejects_duckdb_glob_paths() { + let error = native_options("/tmp/*.parquet".to_string(), None, None) + .expect_err("glob path should be rejected"); + assert!(error.contains("glob paths are not allowed")); + + let bracket_pattern = format!( + "{}/geolibre-native-duckdb-[{}].parquet", + std::env::temp_dir().display(), + std::process::id() + ); + let error = native_options(bracket_pattern, None, None) + .expect_err("bracket glob should be rejected"); + assert!(error.contains("glob paths are not allowed")); + } + + #[test] + fn native_options_allows_literal_brackets_in_existing_paths() { + let path = format!( + "{}/geolibre-native-duckdb-[literal]-{}.parquet", + std::env::temp_dir().display(), + std::process::id() + ); + std::fs::write(&path, []).expect("create literal bracket file"); + let options = native_options(path.clone(), None, None).expect("native options"); + assert_eq!(options.path, path); + let _ = std::fs::remove_file(path); + } + + #[test] + fn read_prj_sidecar_crs_returns_trimmed_wkt() { + let suffix = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time before Unix epoch") + .as_nanos(); + let base = std::env::temp_dir().join(format!( + "geolibre-native-prj-{suffix}-{}", + std::process::id() + )); + let shp_path = base.with_extension("shp"); + let prj_path = base.with_extension("prj"); + std::fs::write( + &prj_path, + " PROJCS[\"British_National_Grid\",GEOGCS[\"GCS_OSGB_1936\"]]\n", + ) + .expect("write prj sidecar"); + + let crs = + read_prj_sidecar_crs(&shp_path.to_string_lossy()).expect("prj sidecar resolves a CRS"); + assert_eq!( + crs, + "PROJCS[\"British_National_Grid\",GEOGCS[\"GCS_OSGB_1936\"]]" + ); + + let _ = std::fs::remove_file(&prj_path); + } + + #[test] + fn read_prj_sidecar_crs_is_none_when_absent_or_empty() { + let suffix = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time before Unix epoch") + .as_nanos(); + let base = std::env::temp_dir().join(format!( + "geolibre-native-prj-missing-{suffix}-{}", + std::process::id() + )); + let shp_path = base.with_extension("shp"); + + assert!(read_prj_sidecar_crs(&shp_path.to_string_lossy()).is_none()); + + let prj_path = base.with_extension("prj"); + std::fs::write(&prj_path, " \n").expect("write empty prj sidecar"); + assert!(read_prj_sidecar_crs(&shp_path.to_string_lossy()).is_none()); + let _ = std::fs::remove_file(&prj_path); + } + + #[test] + fn read_prj_sidecar_crs_matches_mixed_case_extension() { + let suffix = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("system time before Unix epoch") + .as_nanos(); + // A unique per-test subdirectory so the directory scan only sees this + // file set, independent of other tests sharing the temp dir. + let dir = std::env::temp_dir().join(format!( + "geolibre-native-prj-case-{suffix}-{}", + std::process::id() + )); + std::fs::create_dir_all(&dir).expect("create test dir"); + // A mixed-case sidecar extension AND basename (`hotspots.shp` vs + // `Hotspots.PRJ`) that neither the `prj` nor `PRJ` fast path matches. + let shp_path = dir.join("hotspots.shp"); + let prj_path = dir.join("Hotspots.PRJ"); + std::fs::write(&prj_path, "PROJCS[\"OSGB\"]\n").expect("write prj sidecar"); + + let crs = read_prj_sidecar_crs(&shp_path.to_string_lossy()) + .expect("mixed-case prj sidecar resolves a CRS"); + assert_eq!(crs, "PROJCS[\"OSGB\"]"); + + let _ = std::fs::remove_dir_all(&dir); + } + + #[test] + fn wkb_detection_uses_preferred_column_name() { + let detected = detect_geometry_column_from_schema(&[ + DescribedColumn { + name: "wkb".to_string(), + column_type: "BLOB".to_string(), + }, + DescribedColumn { + name: "geometry".to_string(), + column_type: "BLOB".to_string(), + }, + ]) + .expect("detect WKB geometry"); + assert_eq!(detected.column, "geometry"); + assert!(detected.is_wkb); + assert!(!detected.is_base64_wkb); + assert!(!detected.requires_base64_wkb_validation); + } + + #[test] + fn wkb_detection_marks_base64_string_geometry_candidates() { + let detected = detect_geometry_column_from_schema(&[ + DescribedColumn { + name: "id".to_string(), + column_type: "BIGINT".to_string(), + }, + DescribedColumn { + name: "geometry".to_string(), + column_type: "VARCHAR".to_string(), + }, + ]) + .expect("detect base64 WKB geometry"); + assert_eq!(detected.column, "geometry"); + assert!(detected.is_wkb); + assert!(detected.is_base64_wkb); + assert!(detected.requires_base64_wkb_validation); + assert_eq!(detected.base64_wkb_candidates, vec!["geometry".to_string()]); + } + + #[test] + fn integer_json_uses_javascript_safe_range() { + assert_eq!( + json_number_or_string_i128(9_007_199_254_740_991), + json!(9_007_199_254_740_991_i64) + ); + assert_eq!( + json_number_or_string_i128(9_007_199_254_740_992), + json!("9007199254740992") + ); + assert_eq!( + json_number_or_string_u128(9_007_199_254_740_992), + json!("9007199254740992") + ); + } + + #[test] + fn out_of_range_time_falls_back_to_raw_microseconds() { + let micros = (u32::MAX as i64 + 1) * 1_000_000; + assert_eq!( + format_time(TimeUnit::Microsecond, micros), + format!("{micros}us") + ); + } + + #[test] + fn geoparquet_crs_reads_primary_column_authority_code() { + let metadata = r#"{ + "version": "1.1.0", + "primary_column": "geom", + "columns": { + "geom": { + "encoding": "WKB", + "crs": { + "type": "ProjectedCRS", + "id": { "authority": "EPSG", "code": 3857 } + } + } + } + }"#; + assert_eq!( + geoparquet_crs_from_metadata(metadata), + Some("EPSG:3857".to_string()) + ); + } +} diff --git a/apps/geolibre-desktop/src-tauri/tauri.android.conf.json b/apps/geolibre-desktop/src-tauri/tauri.android.conf.json new file mode 100644 index 0000000..6ddecc4 --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/tauri.android.conf.json @@ -0,0 +1,7 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "GeoLibre", + "bundle": { + "resources": [] + } +} diff --git a/apps/geolibre-desktop/src-tauri/tauri.conf.json b/apps/geolibre-desktop/src-tauri/tauri.conf.json new file mode 100644 index 0000000..c8cf627 --- /dev/null +++ b/apps/geolibre-desktop/src-tauri/tauri.conf.json @@ -0,0 +1,41 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "GeoLibre Desktop", + "version": "2.0.0", + "identifier": "org.geolibre.desktop", + "build": { + "beforeDevCommand": "npm run dev", + "devUrl": "http://localhost:5173", + "beforeBuildCommand": "npm run build", + "frontendDist": "../dist" + }, + "app": { + "windows": [ + { + "create": false, + "title": "GeoLibre Desktop", + "width": 1280, + "height": 800, + "resizable": true, + "fullscreen": false + } + ], + "security": { + "csp": "default-src 'self'; connect-src 'self' blob: https: http://127.0.0.1:* http://localhost:* wss://collab.geolibre.app ws://127.0.0.1:* ws://localhost:*; img-src 'self' data: blob: https:; media-src 'self' blob: https:; style-src 'self' 'unsafe-inline'; script-src 'self' blob: 'unsafe-eval' 'wasm-unsafe-eval' https://cdn.jsdelivr.net/npm/ https://cdn.jsdelivr.net/pyodide/ https://accounts.google.com; child-src 'self' http://127.0.0.1:* http://localhost:* https://accounts.google.com https://www.google.com; frame-src 'self' http://127.0.0.1:* http://localhost:* https://accounts.google.com https://www.google.com; worker-src blob: 'self'", + "capabilities": ["default"] + } + }, + "bundle": { + "active": true, + "targets": "all", + "resources": ["../../../backend/geolibre_server"], + "icon": [ + "icons/icon.png", + "icons/32x32.png", + "icons/128x128.png", + "icons/128x128@2x.png", + "icons/icon.icns", + "icons/icon.ico" + ] + } +} diff --git a/apps/geolibre-desktop/src/App.tsx b/apps/geolibre-desktop/src/App.tsx new file mode 100644 index 0000000..d174101 --- /dev/null +++ b/apps/geolibre-desktop/src/App.tsx @@ -0,0 +1,49 @@ +import { DesktopShell } from "./components/layout/DesktopShell"; +import { OnboardingDialog } from "./components/layout/OnboardingDialog"; +import { UpdateNotificationModal } from "./components/layout/UpdateNotificationModal"; +import { useDesktopSettingsPersistence } from "./hooks/useDesktopSettings"; +import { useLayoutOptions } from "./hooks/useLayoutOptions"; +import { useProjectUrlLoader } from "./hooks/useProjectUrlLoader"; +import { useBeforeUnloadGuard } from "./hooks/useBeforeUnloadGuard"; +import { useRecentProjectsPersistence } from "./hooks/useRecentProjectsPersistence"; +import { useRuntimeEnvironmentVariables } from "./hooks/useRuntimeEnvironmentVariables"; +import { useStartupUpdateCheck } from "./hooks/useStartupUpdateCheck"; +import { useThemeMode } from "./hooks/useThemeMode"; +import { useThemeScheme } from "./hooks/useThemeScheme"; +import { useUiProfileBootstrap } from "./hooks/useUiProfileBootstrap"; +import { useUndoRedoShortcuts } from "./hooks/useUndoRedoShortcuts"; + +export default function App() { + const layoutOptions = useLayoutOptions(); + const { themeMode, toggleThemeMode } = useThemeMode(); + const projectUrlLoadState = useProjectUrlLoader(); + const { showOnboarding, dismissOnboarding } = useUiProfileBootstrap(); + const { + pending: pendingUpdate, + remindLater, + skipVersion, + } = useStartupUpdateCheck(); + + useDesktopSettingsPersistence(); + useThemeScheme(); + useRecentProjectsPersistence(); + useRuntimeEnvironmentVariables(); + useUndoRedoShortcuts(); + useBeforeUnloadGuard(); + return ( + <> + + + + + ); +} diff --git a/apps/geolibre-desktop/src/components/CollapsibleSection.tsx b/apps/geolibre-desktop/src/components/CollapsibleSection.tsx new file mode 100644 index 0000000..b937627 --- /dev/null +++ b/apps/geolibre-desktop/src/components/CollapsibleSection.tsx @@ -0,0 +1,49 @@ +import { cn } from "@geolibre/ui"; +import { ChevronRight } from "lucide-react"; +import { useId, useState, type ReactNode } from "react"; + +interface CollapsibleSectionProps { + /** Heading text shown on the toggle row. */ + title: string; + /** Whether the body starts expanded (default: collapsed). */ + defaultOpen?: boolean; + children: ReactNode; +} + +/** + * A picker section whose body collapses behind its heading, to keep a long list + * from stretching the panel. The heading acts as the toggle and rotates a + * chevron; the body is unmounted while collapsed. Styled to match the plain + * section headings it sits alongside. + */ +export function CollapsibleSection({ + title, + defaultOpen = false, + children, +}: CollapsibleSectionProps) { + const [open, setOpen] = useState(defaultOpen); + const contentId = useId(); + return ( +
+ + {/* Wrapper is always present so aria-controls resolves; children mount + only when expanded. */} +
{open ? children : null}
+
+ ); +} diff --git a/apps/geolibre-desktop/src/components/command/CommandPalette.tsx b/apps/geolibre-desktop/src/components/command/CommandPalette.tsx new file mode 100644 index 0000000..ba88ccf --- /dev/null +++ b/apps/geolibre-desktop/src/components/command/CommandPalette.tsx @@ -0,0 +1,187 @@ +import { Dialog, DialogContent, DialogTitle } from "@geolibre/ui"; +import { Search } from "lucide-react"; +import { + type KeyboardEvent as ReactKeyboardEvent, + useEffect, + useMemo, + useRef, + useState, +} from "react"; +import { + type Command, + filterCommands, + formatShortcut, + isMacPlatform, +} from "../../lib/commands"; + +interface CommandPaletteProps { + open: boolean; + commands: Command[]; + onOpenChange: (open: boolean) => void; +} + +/** + * A searchable command palette (Cmd/Ctrl-K) built from the shared command + * registry. Type to filter, navigate with arrow keys, and press Enter to run + * the highlighted command. + */ +export function CommandPalette({ + open, + commands, + onOpenChange, +}: CommandPaletteProps) { + const [query, setQuery] = useState(""); + const [activeIndex, setActiveIndex] = useState(0); + const listRef = useRef(null); + const isMac = useMemo(() => isMacPlatform(), []); + const listboxId = "command-palette-listbox"; + const optionId = (command: Command) => `command-option-${command.id}`; + + const filtered = useMemo( + () => filterCommands(commands, query), + [commands, query], + ); + const activeCommand = filtered[activeIndex]; + + // Reset the query each time the palette opens so it always starts fresh. + useEffect(() => { + if (open) { + setQuery(""); + setActiveIndex(0); + } + }, [open]); + + // Keep the highlight within bounds as the filtered list shrinks/grows. + useEffect(() => { + setActiveIndex((index) => + filtered.length === 0 ? 0 : Math.min(index, filtered.length - 1), + ); + }, [filtered.length]); + + // Scroll the highlighted row into view as the user navigates. + useEffect(() => { + const list = listRef.current; + if (!list) return; + const active = list.querySelector('[data-active="true"]'); + active?.scrollIntoView({ block: "nearest" }); + }, [activeIndex, filtered]); + + const runCommand = (command: Command) => { + onOpenChange(false); + command.run(); + }; + + const handleInputKeyDown = (event: ReactKeyboardEvent) => { + if (event.key === "ArrowDown") { + event.preventDefault(); + setActiveIndex((index) => + filtered.length === 0 ? 0 : (index + 1) % filtered.length, + ); + } else if (event.key === "ArrowUp") { + event.preventDefault(); + setActiveIndex((index) => + filtered.length === 0 + ? 0 + : (index - 1 + filtered.length) % filtered.length, + ); + } else if (event.key === "Home") { + event.preventDefault(); + setActiveIndex(0); + } else if (event.key === "End") { + event.preventDefault(); + setActiveIndex(Math.max(0, filtered.length - 1)); + } else if (event.key === "Enter") { + event.preventDefault(); + const command = filtered[activeIndex]; + if (command) runCommand(command); + } + }; + + return ( + + + Command palette +
+ + { + setQuery(event.target.value); + setActiveIndex(0); + }} + onKeyDown={handleInputKeyDown} + /> +
+
+ {filtered.length === 0 ? ( +

+ No matching commands +

+ ) : ( + filtered.map((command, index) => { + const Icon = command.icon; + const previousGroup = filtered[index - 1]?.group; + const showGroup = command.group !== previousGroup; + const isActive = index === activeIndex; + return ( +
+ {showGroup ? ( +
+ {command.group} +
+ ) : null} + +
+ ); + }) + )} +
+
+
+ ); +} diff --git a/apps/geolibre-desktop/src/components/command/KeyboardShortcutsDialog.tsx b/apps/geolibre-desktop/src/components/command/KeyboardShortcutsDialog.tsx new file mode 100644 index 0000000..ba5c786 --- /dev/null +++ b/apps/geolibre-desktop/src/components/command/KeyboardShortcutsDialog.tsx @@ -0,0 +1,137 @@ +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@geolibre/ui"; +import { useMemo } from "react"; +import { + type Command, + type Shortcut, + PALETTE_SHORTCUT, + SHORTCUTS_HELP_SHORTCUT, + formatShortcut, + isMacPlatform, +} from "../../lib/commands"; + +interface KeyboardShortcutsDialogProps { + open: boolean; + commands: Command[]; + onOpenChange: (open: boolean) => void; +} + +interface ShortcutRow { + id: string; + label: string; + /** A command-style shortcut, formatted per platform. */ + shortcut?: Shortcut; + /** A pre-rendered key label, for keys handled natively by MapLibre. */ + display?: string; +} + +/** + * Map navigation keys handled by MapLibre's own keyboard interaction (not the + * command registry), listed for discoverability. These mirror Google Earth's + * navigation keys and only work while the map canvas has focus. + */ +const MAP_NAVIGATION_ROWS: ShortcutRow[] = [ + { id: "nav.zoom-in", label: "Zoom in", display: "+" }, + { id: "nav.zoom-out", label: "Zoom out", display: "−" }, + { id: "nav.pan", label: "Pan", display: "← ↑ ↓ →" }, + { id: "nav.rotate", label: "Rotate", display: "⇧ ← / →" }, + { id: "nav.tilt", label: "Tilt", display: "⇧ ↑ / ↓" }, +]; + +/** + * A cheat sheet (opened with `?`) listing every global keyboard shortcut, + * grouped the same way as the command palette. + */ +export function KeyboardShortcutsDialog({ + open, + commands, + onOpenChange, +}: KeyboardShortcutsDialogProps) { + const isMac = useMemo(() => isMacPlatform(), []); + + const groups = useMemo(() => { + const ordered: Array<{ group: string; rows: ShortcutRow[] }> = []; + const indexByGroup = new Map(); + const pushRow = (group: string, row: ShortcutRow) => { + let position = indexByGroup.get(group); + if (position === undefined) { + position = ordered.length; + indexByGroup.set(group, position); + ordered.push({ group, rows: [] }); + } + ordered[position].rows.push(row); + }; + + // The palette and cheat-sheet shortcuts are not commands, so list them + // first under a "General" group. + pushRow("General", { + id: "general.open-command-palette", + label: "Open command palette", + shortcut: PALETTE_SHORTCUT, + }); + pushRow("General", { + id: "general.show-keyboard-shortcuts", + label: "Show keyboard shortcuts", + shortcut: SHORTCUTS_HELP_SHORTCUT, + }); + + for (const command of commands) { + if (command.shortcut) { + pushRow(command.group, { + id: command.id, + label: command.title, + shortcut: command.shortcut, + }); + } + } + + // Append the MapLibre-native navigation keys as a final, display-only group. + for (const row of MAP_NAVIGATION_ROWS) { + pushRow("Map navigation", row); + } + return ordered; + }, [commands]); + + return ( + + + + Keyboard shortcuts + + Press {formatShortcut(PALETTE_SHORTCUT, isMac)} to search every + action in the command palette. + + +
+ {groups.map(({ group, rows }) => ( +
+

+ {group} +

+
    + {rows.map((row) => ( +
  • + {row.label} + + {row.shortcut + ? formatShortcut(row.shortcut, isMac) + : row.display} + +
  • + ))} +
+
+ ))} +
+
+
+ ); +} diff --git a/apps/geolibre-desktop/src/components/common/error-boundaries.tsx b/apps/geolibre-desktop/src/components/common/error-boundaries.tsx new file mode 100644 index 0000000..f11dea6 --- /dev/null +++ b/apps/geolibre-desktop/src/components/common/error-boundaries.tsx @@ -0,0 +1,189 @@ +import { + Button, + ErrorBoundary, + type ErrorBoundaryFallbackProps, +} from "@geolibre/ui"; +import { AlertTriangle, RefreshCw, X } from "lucide-react"; +import type { ErrorInfo, ReactNode } from "react"; +import { useTranslation } from "react-i18next"; + +import { appendDiagnostic } from "../../lib/diagnostics"; + +/** + * Records a boundary-caught error in the diagnostics panel so it is visible to + * the user (and copyable for bug reports) instead of only landing in the + * console. + * + * Note: in development React re-throws boundary-caught errors as a synthetic + * `window.error` event, which `installDiagnosticsCapture` also records — so + * each error shows up twice in the Diagnostics panel in dev mode. Production + * builds (where React does not re-throw) record it once. + */ +function reportBoundaryError( + label: string, + error: Error, + info?: ErrorInfo, +): void { + appendDiagnostic({ + category: "runtime", + level: "error", + message: `${label} crashed: ${error.message}`, + detail: [error.stack, info?.componentStack] + .filter((part): part is string => Boolean(part)) + .join("\n\n"), + source: label, + }); +} + +export { reportBoundaryError }; + +/** + * Top-level boundary. A render error anywhere not caught by a more specific + * boundary lands here and shows a full-screen recovery panel rather than a + * blank page. Reloading is the surest recovery for an error this high in the + * tree, so the primary action reloads the app; "Try again" attempts an in-place + * recovery without losing unsaved work. + */ +export function AppErrorBoundary({ children }: { children: ReactNode }) { + return ( + reportBoundaryError("Application", error, info)} + fallback={({ error, reset }) => ( +
+ +
+

Something went wrong

+

+ GeoLibre hit an unexpected error and could not continue. Your work + may be unsaved — try recovering before reloading. +

+

+ {/* Non-Error throws (common from plugin code) have no .message. */} + {error.message || String(error)} +

+
+
+ + +
+
+ )} + > + {children} +
+ ); +} + +/** + * Boundary for an individual region of the shell (a panel, the toolbar, the map + * surface, etc.). A crash in one region shows a compact inline notice and a + * retry button while the rest of the app keeps working. `resetKeys` lets the + * region recover automatically when its driving inputs change. + */ +export function SectionErrorBoundary({ + label, + children, + fallbackClassName, + resetKeys, + onClose, +}: { + label: string; + children: ReactNode; + /** + * Class applied to the error fallback container. It has no effect during + * normal rendering — the boundary injects no wrapper around its children. + */ + fallbackClassName?: string; + resetKeys?: readonly unknown[]; + /** + * When the crashed section owns its own close control (a dockable panel whose + * header is replaced by this fallback), pass a closer so the user is not + * stranded when Retry keeps re-throwing — the fallback then offers a "Close" + * action that dismisses the section entirely. + */ + onClose?: () => void; +}) { + return ( + reportBoundaryError(label, error, info)} + fallback={({ reset }) => ( + + )} + > + {children} + + ); +} + +/** + * Boundary for a non-essential overlay (e.g. a badge floating over the map): on + * error it renders nothing rather than a visible fallback, but still reports to + * diagnostics. Use it so a faulty overlay can never take down the surrounding + * UI it shares a subtree with. + */ +export function SilentErrorBoundary({ + label, + children, +}: { + label: string; + children: ReactNode; +}) { + return ( + reportBoundaryError(label, error, info)} + fallback={() => null} + > + {children} + + ); +} + +function SectionErrorFallback({ + label, + reset, + className, + onClose, +}: Pick & { + label: string; + className?: string; + onClose?: () => void; +}) { + const { t } = useTranslation(); + return ( +
+ +

+ {label} failed to render. The rest of GeoLibre is still available. +

+
+ + {onClose ? ( + + ) : null} +
+
+ ); +} diff --git a/apps/geolibre-desktop/src/components/layout/AboutDialog.tsx b/apps/geolibre-desktop/src/components/layout/AboutDialog.tsx new file mode 100644 index 0000000..de7c548 --- /dev/null +++ b/apps/geolibre-desktop/src/components/layout/AboutDialog.tsx @@ -0,0 +1,327 @@ +import { + Button, + type ButtonProps, + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@geolibre/ui"; +import { CheckCircle2, ExternalLink, Info, Map, RefreshCw } from "lucide-react"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import { openExternalLink } from "../../lib/open-external"; +import { + APP_VERSION, + compareVersions, + fetchLatestRelease, + IS_STORE_BUILD, + UPDATE_URL, + UpdateCheckError, +} from "../../lib/updates"; +import { ReleaseNotes } from "./ReleaseNotes"; +import { UpdateInstructions } from "./UpdateInstructions"; + +const LINKS = [ + { + labelKey: "about.homePage", + href: "https://geolibre.app", + }, + { + labelKey: "about.githubRepository", + href: "https://github.com/opengeos/GeoLibre", + }, +] as const; + +type UpdateStatus = "idle" | "checking" | "current" | "available" | "error"; + +interface AboutDialogProps { + checkForUpdatesRequest?: number; + open?: boolean; + onOpenChange?: (open: boolean) => void; + renderTrigger?: boolean; + buttonClassName?: string; + buttonSize?: ButtonProps["size"]; + iconClassName?: string; + showLabels?: boolean; +} + +export function AboutDialog({ + checkForUpdatesRequest = 0, + open, + onOpenChange, + renderTrigger = true, + buttonClassName, + buttonSize = "sm", + iconClassName, + showLabels = true, +}: AboutDialogProps) { + const { t } = useTranslation(); + const [internalOpen, setInternalOpen] = useState(false); + const [updateStatus, setUpdateStatus] = useState("idle"); + const [latestVersion, setLatestVersion] = useState(null); + const [latestNotes, setLatestNotes] = useState(""); + const [latestUrl, setLatestUrl] = useState(UPDATE_URL); + const [updateError, setUpdateError] = useState(null); + const abortRef = useRef(null); + const handledCheckForUpdatesRequestRef = useRef(0); + const wasOpenRef = useRef(false); + const dialogOpen = open ?? internalOpen; + + useEffect(() => () => abortRef.current?.abort(), []); + + const resetUpdateState = useCallback(() => { + setUpdateStatus("idle"); + setLatestVersion(null); + setLatestNotes(""); + setLatestUrl(UPDATE_URL); + setUpdateError(null); + }, []); + + const describeUpdateError = useCallback( + (error: unknown): string => { + if (error instanceof UpdateCheckError) { + switch (error.code) { + case "rateLimit": + return t("updates.error.rateLimit"); + case "http": + return t("updates.error.http", { status: error.status }); + case "noTag": + return t("updates.error.noTag"); + case "network": + default: + return t("updates.error.network"); + } + } + // Non-UpdateCheckError (an unexpected failure): reuse the network message + // rather than duplicating the generic header string shown above it. + return t("updates.error.network"); + }, + [t], + ); + + const handleCheckForUpdates = async () => { + // Belt-and-braces: the Store build hides every trigger for this flow; this + // guard ensures it never reaches out to GitHub even if one slips through + // (policy 10.2.5). + if (IS_STORE_BUILD) return; + abortRef.current?.abort(); + const controller = new AbortController(); + abortRef.current = controller; + setUpdateStatus("checking"); + setLatestVersion(null); + setLatestNotes(""); + setLatestUrl(UPDATE_URL); + setUpdateError(null); + + try { + const release = await fetchLatestRelease(controller.signal); + setLatestVersion(release.version); + setLatestNotes(release.notes); + setLatestUrl(release.url); + setUpdateStatus( + compareVersions(APP_VERSION, release.version) < 0 + ? "available" + : "current", + ); + } catch (error) { + if (error instanceof Error && error.name === "AbortError") return; + console.error("Failed to check for updates", error); + setUpdateStatus("error"); + setUpdateError(describeUpdateError(error)); + } + }; + + useEffect(() => { + if (dialogOpen && !wasOpenRef.current) resetUpdateState(); + wasOpenRef.current = dialogOpen; + }, [dialogOpen, resetUpdateState]); + + // Read the latest handler through a ref so the effect can depend only on + // the command counter; the update check should run exactly once for each + // increment. Invariant: call sites must increment checkForUpdatesRequest + // only while also opening the dialog; an increment made while the dialog + // stays closed would fire the check on the next open instead. + const handleCheckForUpdatesRef = useRef(handleCheckForUpdates); + handleCheckForUpdatesRef.current = handleCheckForUpdates; + + useEffect(() => { + if ( + !dialogOpen || + checkForUpdatesRequest === 0 || + checkForUpdatesRequest === handledCheckForUpdatesRequestRef.current + ) { + return; + } + handledCheckForUpdatesRequestRef.current = checkForUpdatesRequest; + void handleCheckForUpdatesRef.current(); + }, [checkForUpdatesRequest, dialogOpen]); + + const handleOpenChange = (nextOpen: boolean) => { + setInternalOpen(nextOpen); + onOpenChange?.(nextOpen); + }; + + return ( + + {renderTrigger ? ( + + + + ) : null} + + + + + {t("about.title")} + + {t("about.description")} + +
+
+ {t("about.version")} + v{APP_VERSION} +
+ {/* The Microsoft Store build omits the entire in-app update flow + so the app updates only through the Store (policy 10.2.5). */} + {!IS_STORE_BUILD && ( + <> + + {updateStatus !== "idle" && updateStatus !== "checking" ? ( +
+ {updateStatus === "current" ? ( +
+ + + {latestVersion + ? t("about.upToDate", { version: latestVersion }) + : t("about.upToDateNoVersion")} + +
+ ) : null} + {updateStatus === "available" ? ( +
+
+ {t("updates.available.title", { + version: latestVersion ?? t("updates.available.fallback"), + })} +
+
+ {t("updates.available.summary", { + current: `v${APP_VERSION}`, + })} +
+
+
+ {t("updates.changelogTitle")} +
+ +
+
+
+ {t("updates.stepsTitle")} +
+ +
+ +
+ ) : null} + {updateStatus === "error" ? ( +
+
+ {t("updates.error.message")} +
+ {updateError ? ( +
+ {updateError} +
+ ) : null} + {/* Group the hint with its button and divide it from the error + message above so the two muted lines do not read as one. */} +
+
+ {t("updates.error.viewDownloadsHint")} +
+ +
+
+ ) : null} +
+ ) : null} + + )} + +
+
+
+ ); +} diff --git a/apps/geolibre-desktop/src/components/layout/AddDataDialog.tsx b/apps/geolibre-desktop/src/components/layout/AddDataDialog.tsx new file mode 100644 index 0000000..ad23bb0 --- /dev/null +++ b/apps/geolibre-desktop/src/components/layout/AddDataDialog.tsx @@ -0,0 +1,171 @@ +import { useAppStore } from "@geolibre/core"; +import type { MapController } from "@geolibre/map"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@geolibre/ui"; +import { Database } from "lucide-react"; +import { useCallback, useMemo, useState, type RefObject } from "react"; +import { useTranslation } from "react-i18next"; +import { AddDataShellProvider } from "./add-data/context"; +import { KIND_I18N_KEY } from "./add-data/constants"; +import { ArcGISSource } from "./add-data/sources/ArcGISSource"; +import { CadSource } from "./add-data/sources/CadSource"; +import { DeckVizSource } from "./add-data/sources/DeckVizSource"; +import { DelimitedTextSource } from "./add-data/sources/DelimitedTextSource"; +import { GeoRssSource } from "./add-data/sources/GeoRssSource"; +import { GpxSource } from "./add-data/sources/GpxSource"; +import { MbtilesSource } from "./add-data/sources/MbtilesSource"; +import { OgcVectorTilesSource } from "./add-data/sources/OgcVectorTilesSource"; +import { PhotosSource } from "./add-data/sources/PhotosSource"; +import { PostgresSource } from "./add-data/sources/PostgresSource"; +import { VideoSource } from "./add-data/sources/VideoSource"; +import { WfsSource } from "./add-data/sources/WfsSource"; +import { WmsSource } from "./add-data/sources/WmsSource"; +import { WmtsSource } from "./add-data/sources/WmtsSource"; +import { XyzSource } from "./add-data/sources/XyzSource"; +import type { AddDataKind } from "./add-data/types"; +import type { OpenAddDataPostgres } from "./add-data/open-add-data"; +import { useMartinConnection } from "./add-data/useMartinConnection"; + +export type { AddDataKind } from "./add-data/types"; + +interface AddDataDialogProps { + kind: AddDataKind | null; + mapControllerRef: RefObject; + onOpenChange: (open: boolean) => void; + /** + * Deck.gl Layer kind to pre-select when the dialog opens as `deckgl-viz` + * (e.g. a "3D model" menu entry opens it on the scenegraph layer type). + */ + initialDeckVizKind?: string; + /** + * Connection (and optional table) to pre-select when the dialog opens as + * `postgres` — set when the Browser panel opens a saved connection or a + * clicked PostGIS table. + */ + initialPostgres?: OpenAddDataPostgres; +} + +/** + * Renders the active data-source subcomponent for the given kind. Each source + * is self-contained: it owns its own form state and submit logic and reads the + * shared services from the dialog shell via context. + */ +function renderSource( + kind: AddDataKind, + initialDeckVizKind: string | undefined, + initialPostgres: OpenAddDataPostgres | undefined, +) { + switch (kind) { + case "xyz": + return ; + case "wms": + return ; + case "wfs": + return ; + case "wmts": + return ; + case "ogc-vector-tiles": + return ; + case "gpx": + return ; + case "georss": + return ; + case "delimited-text": + return ; + case "cad": + return ; + case "photos": + return ; + case "mbtiles": + return ; + case "arcgis": + return ; + case "postgres": + return ; + case "video": + return ; + case "deckgl-viz": + return ; + default: + return null; + } +} + +/** + * Shell for the Add Data dialog. Owns the cross-cutting state (submit-in-progress, + * the Martin connection that must survive source remounts) and exposes shared + * services to the per-source subcomponents through context. + */ +export function AddDataDialog({ + kind, + mapControllerRef, + onOpenChange, + initialDeckVizKind, + initialPostgres, +}: AddDataDialogProps) { + const { t } = useTranslation(); + const open = kind !== null; + const addLayer = useAppStore((s) => s.addLayer); + const existingLayers = useAppStore((s) => s.layers); + const [isSubmitting, setIsSubmitting] = useState(false); + const martin = useMartinConnection(); + + const title = kind + ? t(`addData.kind.${KIND_I18N_KEY[kind]}.label`) + : t("addData.title"); + const description = kind + ? t(`addData.kind.${KIND_I18N_KEY[kind]}.description`) + : ""; + + const closeDialog = useCallback(() => { + martin.stopTransient(); + onOpenChange(false); + }, [martin, onOpenChange]); + + const handleOpenChange = (next: boolean) => { + if (!next && isSubmitting) return; + if (!next) martin.stopTransient(); + onOpenChange(next); + }; + + // Memoized so context consumers (the source forms) only re-render when shell + // state actually changes, not on every shell render. Effective because + // `martin` and `closeDialog` are stable across renders (see their hooks). + const contextValue = useMemo( + () => ({ + mapControllerRef, + addLayer, + existingLayers, + isSubmitting, + setIsSubmitting, + closeDialog, + martin, + }), + [mapControllerRef, addLayer, existingLayers, isSubmitting, closeDialog, martin], + ); + + return ( + + + + + + {title} + + {description} + + + {kind ? ( + + {renderSource(kind, initialDeckVizKind, initialPostgres)} + + ) : null} + + + ); +} diff --git a/apps/geolibre-desktop/src/components/layout/AddNetcdfDialog.tsx b/apps/geolibre-desktop/src/components/layout/AddNetcdfDialog.tsx new file mode 100644 index 0000000..2ce9fe1 --- /dev/null +++ b/apps/geolibre-desktop/src/components/layout/AddNetcdfDialog.tsx @@ -0,0 +1,476 @@ +import { useRef, useState, type FormEvent } from "react"; +import { useTranslation } from "react-i18next"; +import { + addCloudNetcdfLayer, + listKerchunkVariables, + loadKerchunkReference, + openLocalNetcdf, + type GeoLibreAppAPI, + type KerchunkRefs, + type KerchunkVariable, + type LocalNetcdfFile, +} from "@geolibre/plugins"; +import { + Button, + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + Input, + Label, + Select, +} from "@geolibre/ui"; +import { Boxes, FileUp } from "lucide-react"; +import { openLocalDataFileWithFallback } from "../../lib/tauri-io"; +import { SampleDataSelect } from "./add-data/shared"; + +// A real sample: NOAA NCEP/NCAR Reanalysis surface air temperature, stored as a +// Cloud-Optimized NetCDF and served from Source Cooperative (CORS-enabled, +// range-request capable). The reference uses a relative chunk URL, so it +// resolves against the manifest's own location. +const SAMPLE_URL = + "https://data.source.coop/giswqs/opengeos/netcdf/air-temperature.kerchunk.json"; + +// Extensions accepted for a local file: classic NetCDF-3 (.nc/.cdf, via +// netcdfjs) and HDF5-backed NetCDF-4/HDF5 (.nc/.nc4/.h5/.hdf5, via h5wasm). +// `.hdf` (usually HDF4) is omitted since neither backend reads it. +const LOCAL_EXTENSIONS = ["nc", "nc4", "cdf", "h5", "hdf5"]; + +/** A renderable variable, shared shape across the cloud and local readers. */ +type RenderableVariable = KerchunkVariable; + +interface AddNetcdfDialogProps { + open: boolean; + appApi: GeoLibreAppAPI; + onOpenChange: (open: boolean) => void; +} + +/** + * Dialog for adding a NetCDF/HDF5 layer. Two sources are supported: a remote + * Cloud-Optimized NetCDF via a kerchunk reference URL (read over HTTP range + * requests), or a local HDF5/NetCDF-4 file decoded in-browser with h5wasm. In + * both cases the user picks a renderable variable (and any leading dimension + * index), and the layer renders through the shared Zarr control. + */ +export function AddNetcdfDialog({ + open, + appApi, + onOpenChange, +}: AddNetcdfDialogProps) { + const { t } = useTranslation(); + const [source, setSource] = useState<"url" | "file">("url"); + const [url, setUrl] = useState(""); + // The local file opened in the WASM filesystem, kept between "Load variables" + // and submit so the (potentially large) decode happens once. Closed on reset. + const [localFile, setLocalFile] = useState(null); + const [fileName, setFileName] = useState(""); + const [variables, setVariables] = useState([]); + const [variable, setVariable] = useState(""); + // The normalized reference from the last successful URL load, reused on submit + // so the (potentially large) manifest is not fetched a second time. + const [loadedRefs, setLoadedRefs] = useState(null); + const [dimIndex, setDimIndex] = useState>({}); + const [climMin, setClimMin] = useState(""); + const [climMax, setClimMax] = useState(""); + const [loadingVars, setLoadingVars] = useState(false); + const [adding, setAdding] = useState(false); + const [error, setError] = useState(null); + const [status, setStatus] = useState(null); + // Incremented on every reset; lets in-flight async handlers detect that the + // dialog was closed/reopened and bail out before stomping fresh state. + const opGen = useRef(0); + // Holds the currently-open local file so it can be closed synchronously on + // reset even if the latest setLocalFile has not flushed to state yet. + const openFileRef = useRef(null); + + const selectedVar = variables.find((v) => v.name === variable); + // Dimensions other than the trailing two (lat/lon) need a fixed index. + const leadingDims = selectedVar + ? selectedVar.dims.slice(0, Math.max(0, selectedVar.dims.length - 2)) + : []; + + const closeOpenFile = () => { + openFileRef.current?.close(); + openFileRef.current = null; + }; + + const reset = () => { + opGen.current += 1; + closeOpenFile(); + setSource("url"); + setUrl(""); + setLocalFile(null); + setFileName(""); + setVariables([]); + setVariable(""); + setLoadedRefs(null); + setDimIndex({}); + setClimMin(""); + setClimMax(""); + setError(null); + setStatus(null); + setLoadingVars(false); + setAdding(false); + }; + + // Invalidate everything tied to the previously loaded source (URL manifest or + // local file) when the input changes, and bump opGen so an in-flight load for + // the old source cannot write back into state. Bumping opGen makes that load's + // finally skip its own setLoadingVars(false), so the flags are cleared here + // too (otherwise Load variables stays disabled). + const invalidateLoadedSource = () => { + opGen.current += 1; + closeOpenFile(); + setLocalFile(null); + setFileName(""); + setVariables([]); + setVariable(""); + setLoadedRefs(null); + setDimIndex({}); + setClimMin(""); + setClimMax(""); + setStatus(null); + setError(null); + setLoadingVars(false); + setAdding(false); + }; + + const applyLoadedVariables = (vars: RenderableVariable[]) => { + setVariables(vars); + setVariable(vars[0].name); + setStatus(`Found ${vars.length} variable${vars.length > 1 ? "s" : ""}.`); + }; + + const handleLoadVariables = async () => { + const gen = opGen.current; + setError(null); + setStatus(null); + // Clear any prior result so the picker hides and "Add layer" disables while + // a new source loads (avoids submitting a variable from the old dataset). + setVariables([]); + setVariable(""); + setLoadedRefs(null); + setLoadingVars(true); + try { + const refs = await loadKerchunkReference(url.trim()); + const vars = listKerchunkVariables(refs); + if (gen !== opGen.current) return; // dialog was closed/reopened + if (vars.length === 0) { + throw new Error( + "No renderable (2-D or higher) variables found in the reference." + ); + } + setLoadedRefs(refs); + applyLoadedVariables(vars); + } catch (err) { + if (gen !== opGen.current) return; + setVariables([]); + setVariable(""); + setError(err instanceof Error ? err.message : String(err)); + } finally { + if (gen === opGen.current) setLoadingVars(false); + } + }; + + const handleChooseLocalFile = async () => { + setError(null); + setStatus(null); + let selected: { data?: ArrayBuffer; path: string } | null; + try { + selected = await openLocalDataFileWithFallback({ + filters: [{ name: "NetCDF / HDF5", extensions: LOCAL_EXTENSIONS }], + accept: LOCAL_EXTENSIONS.map((ext) => `.${ext}`).join(","), + readBinary: true, + }); + } catch (err) { + setError(err instanceof Error ? err.message : String(err)); + return; + } + if (!selected?.data) return; // dialog dismissed + + // Any prior open file (or URL result) is now stale; bump opGen and close it. + invalidateLoadedSource(); + const gen = opGen.current; + setFileName(selected.path); + setLoadingVars(true); + // Held outside the try so a throw after open (e.g. listVariables on a + // corrupt dataset) still closes the WASM file handle in the catch. + let file: LocalNetcdfFile | null = null; + try { + file = await openLocalNetcdf(selected.data); + const vars = file.listVariables(); + if (gen !== opGen.current) { + file.close(); // dialog was closed/reopened while decoding + return; + } + if (vars.length === 0) { + throw new Error(t("addData.netcdf.errorNoVariables")); + } + openFileRef.current = file; + setLocalFile(file); + applyLoadedVariables(vars); + } catch (err) { + // Close the just-opened handle unless it was stored for later use. + if (file && openFileRef.current !== file) file.close(); + if (gen !== opGen.current) return; + setError(err instanceof Error ? err.message : String(err)); + } finally { + if (gen === opGen.current) setLoadingVars(false); + } + }; + + const handleSubmit = async (event: FormEvent) => { + event.preventDefault(); + if (!variable) return; + const gen = opGen.current; + setError(null); + setAdding(true); + try { + const selector: Record = {}; + for (const dim of leadingDims) { + // Zarr indices are non-negative integers; clamp/truncate user input. + const parsed = Number(dimIndex[dim] ?? "0"); + selector[dim] = Number.isFinite(parsed) ? Math.max(0, Math.trunc(parsed)) : 0; + } + const min = climMin.trim() === "" ? undefined : Number(climMin); + const max = climMax.trim() === "" ? undefined : Number(climMax); + const clim = + min !== undefined && + max !== undefined && + Number.isFinite(min) && + Number.isFinite(max) && + min < max + ? ([min, max] as [number, number]) + : undefined; + + if (source === "file") { + const file = localFile; + if (!file) throw new Error(t("addData.netcdf.errorNoFile")); + // Yield once so the "Adding..." state paints before the synchronous, + // CPU-heavy decode/base64-encode of a potentially large local grid. + await new Promise((resolve) => setTimeout(resolve, 0)); + const { refs } = file.buildLayerRefs(variable, selector); + // Use just the file's base name (fileName is a full path on desktop) so + // the derived layer name is clean on every platform. Encode it so a name + // with URL-special chars (#, ?, %) survives layerNameFromUrl's new + // URL(...) parse; that helper decodes it again for the display name. + const baseName = fileName.split(/[\\/]/).pop() || "netcdf"; + await addCloudNetcdfLayer(appApi, { + url: `local:${encodeURIComponent(baseName)}`, + refs, + variable, + clim, + }); + } else { + await addCloudNetcdfLayer(appApi, { + url: url.trim(), + refs: loadedRefs ?? undefined, + variable, + selector: leadingDims.length > 0 ? selector : undefined, + clim, + }); + } + if (gen !== opGen.current) return; // dialog was closed/reopened + onOpenChange(false); + reset(); + } catch (err) { + if (gen !== opGen.current) return; + setError(err instanceof Error ? err.message : String(err)); + } finally { + if (gen === opGen.current) setAdding(false); + } + }; + + return ( + { + if (!next) reset(); + onOpenChange(next); + }} + > + + + + + {t("addData.netcdf.title")} + + + {t("addData.netcdf.description")} + + + +
+ {/* The title/description and the local-file cohort of strings added + here are routed through t(). The older Cloud-URL prose (kerchunk + URL label, variable/color inputs, buttons) predates the Add Data + i18n catalog and remains English pre-existing debt, out of scope + for this change. */} +
+ + +
+ + {source === "url" ? ( + <> + { + invalidateLoadedSource(); + setUrl(sampleUrl); + }} + /> +
+ +
+ { + invalidateLoadedSource(); + setUrl(event.target.value); + }} + /> + +
+

+ Choose a sample dataset above, or paste your own kerchunk + reference URL, then click Load variables. +

+
+ + ) : ( +
+ + + {fileName && ( +

{fileName}

+ )} +

+ {t("addData.netcdf.localFileHelp")} +

+
+ )} + + {variables.length > 0 && ( +
+ + +
+ )} + + {leadingDims.map((dim) => ( +
+ + + setDimIndex((prev) => ({ + ...prev, + [dim]: event.target.value, + })) + } + /> +
+ ))} + + {variables.length > 0 && ( +
+
+ + setClimMin(event.target.value)} + /> +
+
+ + setClimMax(event.target.value)} + /> +
+
+ )} + + {error &&

{error}

} + {status && !error && ( +

{status}

+ )} + +
+ + +
+ +
+
+ ); +} diff --git a/apps/geolibre-desktop/src/components/layout/BoundsRestrictionIndicator.tsx b/apps/geolibre-desktop/src/components/layout/BoundsRestrictionIndicator.tsx new file mode 100644 index 0000000..1a86bea --- /dev/null +++ b/apps/geolibre-desktop/src/components/layout/BoundsRestrictionIndicator.tsx @@ -0,0 +1,50 @@ +import { useAppStore } from "@geolibre/core"; +import { Lock } from "lucide-react"; +import { useTranslation } from "react-i18next"; + +/** + * Subtle corner badge that appears on the map only while the project's + * "Restrict map bounds" preference is enabled. It gives users a visible cue + * that panning/zooming is intentionally constrained (rather than the app being + * frozen), with a tooltip pointing them back to Settings to change it. + * + * Anchored to the bottom-left corner: the top-left corner is where most map + * control plugins (GeoEditor, Basemaps, NASA Earthdata, ...) cluster, so a + * top-left badge overlapped and hid the first plugin button. The small + * bottom offset keeps it clear of the bottom-left scale control when that is + * enabled. The other bottom-left occupant, the collaboration status badge + * (CollaborationStatusBadge), already accounts for this badge: it lifts itself + * to `bottom-20` (above this `bottom-12`) when a session is active and bounds + * are restricted. If yet another bottom-left control is added, revisit both. + * (The KnowledgeCardPanel also opens bottom-left at `bottom-12`, but as a large + * transient `z-20` panel it overlays these small `z-10` badges while open rather + * than stacking with them.) + */ +export function BoundsRestrictionIndicator() { + const { t } = useTranslation(); + const restrictBounds = useAppStore((s) => s.preferences.map.restrictBounds); + + if (!restrictBounds) { + return null; + } + + const tooltip = t("map.boundsRestrictedTooltip"); + + return ( +
+
+ ); +} diff --git a/apps/geolibre-desktop/src/components/layout/CollaborateDialog.tsx b/apps/geolibre-desktop/src/components/layout/CollaborateDialog.tsx new file mode 100644 index 0000000..725c2d0 --- /dev/null +++ b/apps/geolibre-desktop/src/components/layout/CollaborateDialog.tsx @@ -0,0 +1,545 @@ +import { useAppStore, type CollaborationMode } from "@geolibre/core"; +import { + Button, + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + Input, + Label, + Select, +} from "@geolibre/ui"; +import { ArrowRight, Check, Copy, Loader2, LogOut, Users } from "lucide-react"; +import { QRCodeSVG } from "qrcode.react"; +import { useEffect, useMemo, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; +import type { CollaborationApi } from "../../hooks/useCollaboration"; +import { CollaborationParticipantRow } from "./CollaborationParticipantRow"; + +// A small fixed palette so participant colors stay distinct and legible. Each +// entry pairs a hex value with a human-readable name for the swatch aria-label. +const COLOR_PALETTE = [ + { hex: "#2563eb", name: "blue" }, + { hex: "#dc2626", name: "red" }, + { hex: "#16a34a", name: "green" }, + { hex: "#d97706", name: "amber" }, + { hex: "#9333ea", name: "purple" }, + { hex: "#0891b2", name: "cyan" }, + { hex: "#db2777", name: "pink" }, + { hex: "#65a30d", name: "lime" }, +]; +const DEFAULT_COLOR = COLOR_PALETTE[0]?.hex ?? "#2563eb"; + +interface CollaborateDialogProps { + open: boolean; + onOpenChange: (open: boolean) => void; + api: CollaborationApi; +} + +/** + * Dialog for starting, joining, and managing a live collaboration session. + * Reads live session state from the store and drives it through the + * {@link CollaborationApi} provided by `useCollaboration`. + */ +export function CollaborateDialog({ + open, + onOpenChange, + api, +}: CollaborateDialogProps) { + const { t } = useTranslation(); + const collaboration = useAppStore((s) => s.collaboration); + const isActive = collaboration.isActive; + + const [name, setName] = useState(""); + const [color, setColor] = useState(DEFAULT_COLOR); + const [mode, setMode] = useState("co-edit"); + const [code, setCode] = useState(""); + // True when the dialog was opened from a `?collab=` invite link, which + // streamlines the layout to a single Join action (the "Start a session" + // controls are irrelevant to an invited participant). Cleared if the user + // explicitly chooses to host instead, or after a failed join, so they can + // still fall back to the full layout. + const [invited, setInvited] = useState(false); + const [busy, setBusy] = useState(false); + const [error, setError] = useState(null); + const [copied, setCopied] = useState<"code" | "link" | null>(null); + const copyTimer = useRef(null); + + // Seed from a prior session (or a ?collab= deep link) when the dialog opens. + useEffect(() => { + if (!open) { + // Forget any prior invite context on close. The `?collab=` code is + // stripped from the URL on first read, so a later manual reopen has no + // invite link and should show the full layout, not the invited view. + setInvited(false); + return; + } + setError(null); + setBusy(false); + setName((prev) => prev || collaboration.selfName); + setColor((prev) => collaboration.selfColor || prev); + const url = new URL(window.location.href); + const fromUrl = url.searchParams.get("collab"); + if (fromUrl) { + setCode(fromUrl); + setInvited(true); + // Strip the code from the address bar (and thus history/referrer) once + // read, so the session code doesn't linger after joining. + url.searchParams.delete("collab"); + window.history.replaceState({}, "", url.toString()); + } + }, [open, collaboration.selfName, collaboration.selfColor]); + + useEffect( + () => () => { + if (copyTimer.current !== null) window.clearTimeout(copyTimer.current); + }, + [], + ); + + const shareLink = useMemo(() => { + if (!collaboration.sessionId) return ""; + const url = new URL(window.location.href); + url.searchParams.set("collab", collaboration.sessionId); + return url.toString(); + }, [collaboration.sessionId]); + + const handleCopy = (kind: "code" | "link", value: string) => { + navigator.clipboard + .writeText(value) + .then(() => { + if (copyTimer.current !== null) window.clearTimeout(copyTimer.current); + setCopied(kind); + copyTimer.current = window.setTimeout(() => setCopied(null), 2000); + }) + .catch(() => { + setError(t("collaborate.copyFailed")); + }); + }; + + const handleStart = async () => { + if (!name.trim()) { + setError(t("collaborate.nameRequired")); + return; + } + setBusy(true); + setError(null); + try { + await api.start(name.trim(), color, mode); + } catch (err) { + // Show a localized message; keep the raw error in the console for + // diagnostics (collab-client throws human-readable English strings). + console.error("[GeoLibre] Collaboration error", err); + setError(t("collaborate.connectFailed")); + } finally { + setBusy(false); + } + }; + + const handleJoin = async () => { + if (!name.trim()) { + setError(t("collaborate.nameRequired")); + return; + } + if (!code.trim()) { + setError(t("collaborate.codeRequired")); + return; + } + setBusy(true); + setError(null); + try { + await api.join(code.trim(), name.trim(), color); + } catch (err) { + // Show a localized message; keep the raw error in the console for + // diagnostics (collab-client throws human-readable English strings). + console.error("[GeoLibre] Collaboration error", err); + setError(t("collaborate.connectFailed")); + // The invite link could not connect (e.g. an expired or invalid code), so + // reveal the full layout and let the user fix the code or host instead. + setInvited(false); + } finally { + setBusy(false); + } + }; + + const handleLeave = () => { + api.leave(); + onOpenChange(false); + }; + + return ( + + + + + + {t("collaborate.title")} + + {t("collaborate.description")} + + + {isActive ? ( + onOpenChange(false)} + onSetMode={api.setMode} + onSetParticipantMode={api.setParticipantMode} + onSetFollowHost={api.setFollowHost} + /> + ) : ( +
+ {/* Name and color feed both actions below, so group them in a + shaded panel above the cards to read as shared profile inputs + rather than belonging to either Start or Join (#706). */} +
+
+ + setName(e.target.value)} + placeholder={t("collaborate.displayNamePlaceholder")} + maxLength={40} + disabled={busy} + autoFocus + /> +
+
+ + {/* Full panel width keeps every swatch on one row instead of + wrapping a lone dot to a second line (#706). */} +
+ {COLOR_PALETTE.map((c) => ( +
+
+
+ + {/* An invited participant (arrived via a `?collab=` link) only needs + to join, so collapse the layout to a single Join action and hide + the "Start a session" controls that are irrelevant to them + (#753). They can still fall back to hosting via the link below. */} + {invited ? ( +
+
+

+ {t("collaborate.invitedHeading")} +

+

+ {t("collaborate.invitedDescription")} +

+
+
+ + setCode(e.target.value)} + placeholder={t("collaborate.sessionCodePlaceholder")} + disabled={busy} + className="font-mono uppercase" + /> +
+ + +
+ ) : ( + <> +
+

+ {t("collaborate.startHeading")} +

+
+ + +
+ +
+ +
+

+ {t("collaborate.joinHeading")} +

+
+ setCode(e.target.value)} + placeholder={t("collaborate.sessionCodePlaceholder")} + disabled={busy} + className="font-mono uppercase" + /> + +
+
+ + )} + + {/* Show local validation errors and connect failures, the latter + arriving asynchronously in the store (the WebSocket handshake + fails after this dialog's call already resolved). */} + {(error || collaboration.error) && ( +

+ {error || collaboration.error} +

+ )} +
+ )} +
+
+ ); +} + +function ActiveSession({ + shareLink, + copied, + onCopy, + onLeave, + onDismiss, + onSetMode, + onSetParticipantMode, + onSetFollowHost, +}: { + shareLink: string; + copied: "code" | "link" | null; + onCopy: (kind: "code" | "link", value: string) => void; + onLeave: () => void; + onDismiss: () => void; + onSetMode: (mode: CollaborationMode) => void; + onSetParticipantMode: (clientId: string, canEdit: boolean) => void; + onSetFollowHost: (enabled: boolean) => void; +}) { + const { t } = useTranslation(); + const collaboration = useAppStore((s) => s.collaboration); + const isHost = collaboration.role === "host"; + + return ( +
+
+ {collaboration.connecting ? ( + <> + + + {t("collaborate.reconnecting")} + + + ) : ( + <> + + + {t("collaborate.connected")} + + + )} +
+ + {/* Cameras are independent by default; a non-host can opt to follow the + host's viewport (presenter mode). */} + {!isHost && ( + + )} + +
+ +
+ + +
+
+ +
+ +
+ + +
+ {/* Only the host invites others, so the scan-to-join QR is host-only. */} + {isHost && ( +
+
+ +
+

+ {t("collaborate.scanToJoin")} +

+
+ )} +
+ +
+ +
    + {collaboration.participants.map((p) => ( + + ))} +
+
+ + {collaboration.error && ( +

+ {collaboration.error} +

+ )} + + {/* Primary way out of the dialog: dismiss it while keeping the session + live, so the host isn't tempted to use the "X" (which they fear ends + the session) to get back to the map (#754). */} + + +
+ {isHost ? ( + + ) : ( + + )} + +
+
+ ); +} diff --git a/apps/geolibre-desktop/src/components/layout/CollaborationParticipantRow.tsx b/apps/geolibre-desktop/src/components/layout/CollaborationParticipantRow.tsx new file mode 100644 index 0000000..d8cf627 --- /dev/null +++ b/apps/geolibre-desktop/src/components/layout/CollaborationParticipantRow.tsx @@ -0,0 +1,100 @@ +import type { CollaborationMode, CollaborationParticipant } from "@geolibre/core"; +import { Eye, Pencil } from "lucide-react"; +import { useTranslation } from "react-i18next"; +import { participantCanEdit } from "../../lib/collab-protocol"; + +interface CollaborationParticipantRowProps { + participant: CollaborationParticipant; + /** Current session mode, used to derive effective edit permission. */ + mode: CollaborationMode; + /** True when this row is the local user (adds a "(you)" tag). */ + isSelf: boolean; + /** True when the viewer is the host and may change this participant's + * permission. A toggle button replaces the read-only indicator. The host's + * own row never shows a control. */ + canManage: boolean; + onSetParticipantMode: (clientId: string, canEdit: boolean) => void; + /** Render the smaller variant used in the on-canvas badge roster. */ + compact?: boolean; +} + +/** + * One participant row shared by the Collaborate dialog roster and the on-canvas + * status-badge roster (#754): color swatch, name, "you" / host tags, and either + * a host-only per-participant permission toggle or a read-only permission + * indicator. `compact` selects the badge's tighter sizing. + */ +export function CollaborationParticipantRow({ + participant: p, + mode, + isSelf, + canManage, + onSetParticipantMode, + compact = false, +}: CollaborationParticipantRowProps) { + const { t } = useTranslation(); + const editable = participantCanEdit(p, mode); + const isHostRow = p.role === "host"; + // The host can pin any guest (never themselves) to view-only / edit. + const showToggle = canManage && !isHostRow; + const permIcon = editable ? ( +